diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index bc7614cda..e5ec2505a 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -1,12 +1,17 @@ //! Input capture driver. +use core::future::Future; use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; use embassy_hal_internal::{into_ref, PeripheralRef}; use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; +use super::CaptureCompareInterruptHandler; use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; use crate::gpio::{AFType, AnyPin, Pull}; +use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; use crate::Peripheral; @@ -65,6 +70,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { _ch2: Option>, _ch3: Option>, _ch4: Option>, + _irq: impl Binding> + 'd, freq: Hertz, counting_mode: CountingMode, ) -> Self { @@ -79,13 +85,9 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details this.inner.start(); - [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] - .iter() - .for_each(|&channel| { - this.inner.set_input_capture_mode(channel, InputCaptureMode::Rising); - - this.inner.set_input_ti_selection(channel, InputTISelection::Normal); - }); + // enable NVIC interrupt + T::CaptureCompareInterrupt::unpend(); + unsafe { T::CaptureCompareInterrupt::enable() }; this } @@ -142,4 +144,88 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { pub fn get_input_interrupt(&self, channel: Channel) -> bool { self.inner.get_input_interrupt(channel) } + + fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { + self.inner.enable_channel(channel, true); + self.inner.set_input_capture_mode(channel, mode); + self.inner.set_input_ti_selection(channel, tisel); + self.inner.clear_input_interrupt(channel); + self.inner.enable_input_interrupt(channel, true); + + InputCaptureFuture { + channel, + phantom: PhantomData, + } + } + + /// Asynchronously wait until the pin sees a rising edge. + pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the pin sees a falling edge. + pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the pin sees any edge. + pub async fn wait_for_any_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the (alternate) pin sees a rising edge. + pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate) + .await + } + + /// Asynchronously wait until the (alternate) pin sees a falling edge. + pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate) + .await + } + + /// Asynchronously wait until the (alternate) pin sees any edge. + pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate) + .await + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputCaptureFuture { + channel: Channel, + phantom: PhantomData, +} + +impl Drop for InputCaptureFuture { + fn drop(&mut self) { + critical_section::with(|_| { + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + + // disable interrupt enable + regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); + }); + } +} + +impl Future for InputCaptureFuture { + type Output = u32; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + T::state().cc_waker[self.channel.index()].register(cx.waker()); + + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + + let dier = regs.dier().read(); + if !dier.ccie(self.channel.index()) { + let val = regs.ccr(self.channel.index()).read().0; + Poll::Ready(val) + } else { + Poll::Pending + } + } } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 9a1fbcd61..4b4929aeb 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,5 +1,8 @@ //! Timers, PWM, quadrature decoder. +use core::marker::PhantomData; +use embassy_sync::waitqueue::AtomicWaker; + #[cfg(not(stm32l0))] pub mod complementary_pwm; pub mod input_capture; @@ -46,8 +49,29 @@ pub enum TimerBits { Bits32, } +struct State { + up_waker: AtomicWaker, + cc_waker: [AtomicWaker; 4], +} + +impl State { + const fn new() -> Self { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + Self { + up_waker: NEW_AW, + cc_waker: [NEW_AW; 4], + } + } +} + +trait SealedInstance: RccPeripheral { + /// Async state for this timer + fn state() -> &'static State; +} + /// Core timer instance. -pub trait CoreInstance: RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait CoreInstance: SealedInstance + 'static { /// Update Interrupt for this timer. type UpdateInterrupt: interrupt::typelevel::Interrupt; @@ -144,6 +168,13 @@ dma_trait!(Ch4Dma, GeneralInstance4Channel); #[allow(unused)] macro_rules! impl_core_timer { ($inst:ident, $bits:expr) => { + impl SealedInstance for crate::peripherals::$inst { + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + } + impl CoreInstance for crate::peripherals::$inst { type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; @@ -286,3 +317,63 @@ foreach_interrupt! { impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; } + +/// Update interrupt handler. +pub struct UpdateInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for UpdateInterruptHandler { + unsafe fn on_interrupt() { + #[cfg(feature = "low-power")] + crate::low_power::on_wakeup_irq(); + + let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); + + // Read TIM interrupt flags. + let sr = regs.sr().read(); + + // Mask relevant interrupts (UIE). + let bits = sr.0 & 0x00000001; + + // Mask all the channels that fired. + regs.dier().modify(|w| w.0 &= !bits); + + // Wake the tasks + if sr.uif() { + T::state().up_waker.wake(); + } + } +} + +/// Capture/Compare interrupt handler. +pub struct CaptureCompareInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler + for CaptureCompareInterruptHandler +{ + unsafe fn on_interrupt() { + #[cfg(feature = "low-power")] + crate::low_power::on_wakeup_irq(); + + let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); + + // Read TIM interrupt flags. + let sr = regs.sr().read(); + + // Mask relevant interrupts (CCIE). + let bits = sr.0 & 0x0000001E; + + // Mask all the channels that fired. + regs.dier().modify(|w| w.0 &= !bits); + + // Wake the tasks + for ch in 0..4 { + if sr.ccif(ch) { + T::state().cc_waker[ch].wake(); + } + } + } +} diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index 17f65c6b9..862a3103b 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs @@ -3,18 +3,19 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::peripherals::PB2; +use embassy_stm32::peripherals; use embassy_stm32::time::khz; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; -use embassy_stm32::timer::Channel; +use embassy_stm32::timer::{self, Channel}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; /// Connect PB2 and PB10 with a 1k Ohm resistor #[embassy_executor::task] -async fn blinky(led: PB2) { +async fn blinky(led: peripherals::PB2) { let mut led = Output::new(led, Level::High, Speed::Low); loop { @@ -28,6 +29,10 @@ async fn blinky(led: PB2) { } } +bind_interrupts!(struct Irqs { + TIM2 => timer::CaptureCompareInterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); @@ -36,16 +41,13 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB2))); let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); - let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, khz(1000), Default::default()); - ic.enable(Channel::Ch3); + let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { - // Check for input capture - if ic.get_input_interrupt(Channel::Ch3) { - let capture_value = ic.get_capture_value(Channel::Ch3); - info!("New capture! {}", capture_value); - } - // Wait a little bit - Timer::after_millis(1).await; + info!("wait for risign edge"); + ic.wait_for_rising_edge(Channel::Ch3).await; + + let capture_value = ic.get_capture_value(Channel::Ch3); + info!("new capture! {}", capture_value); } }