From 8f194122d3a7c9cc7c607478f799653b5c0e6dda Mon Sep 17 00:00:00 2001 From: Hatim Thayyil Date: Thu, 5 Dec 2024 20:34:26 +0000 Subject: [PATCH 1/4] add low level async module for timers This commits collects together a logical unit of change from brunob45's work on async timers. Relevant examples are also updated. --- embassy-stm32/src/timer/input_capture.rs | 54 ++----------- embassy-stm32/src/timer/ll_async.rs | 95 +++++++++++++++++++++++ embassy-stm32/src/timer/mod.rs | 55 +++++-------- examples/stm32f1/src/bin/input_capture.rs | 6 +- examples/stm32f1/src/bin/pwm_input.rs | 2 +- examples/stm32f4/src/bin/input_capture.rs | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 2 +- examples/stm32g0/src/bin/input_capture.rs | 2 +- examples/stm32g0/src/bin/pwm_input.rs | 2 +- 9 files changed, 127 insertions(+), 93 deletions(-) create mode 100644 embassy-stm32/src/timer/ll_async.rs diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 341ac2c042..e16e5ee0e1 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -1,17 +1,12 @@ //! 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::ll_async::TimerEventFuture; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; -use super::{ - CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, - GeneralInstance4Channel, -}; +use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, InterruptHandler}; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; @@ -68,7 +63,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { _ch2: Option>, _ch3: Option>, _ch4: Option>, - _irq: impl Binding> + 'd, + _irq: impl Binding> + 'd, freq: Hertz, counting_mode: CountingMode, ) -> Self { @@ -125,7 +120,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { self.inner.get_input_interrupt(channel) } - fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { + fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> TimerEventFuture { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode self.inner.set_input_ti_selection(channel, tisel); @@ -133,12 +128,10 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { self.inner.set_input_capture_mode(channel, mode); self.inner.set_input_capture_prescaler(channel, 0); self.inner.enable_channel(channel, true); + self.inner.clear_input_interrupt(channel); self.inner.enable_input_interrupt(channel, true); - InputCaptureFuture { - channel, - phantom: PhantomData, - } + TimerEventFuture::new(channel.into()) } /// Asynchronously wait until the pin sees a rising edge. @@ -177,38 +170,3 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { .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/ll_async.rs b/embassy-stm32/src/timer/ll_async.rs new file mode 100644 index 0000000000..c6ba46623a --- /dev/null +++ b/embassy-stm32/src/timer/ll_async.rs @@ -0,0 +1,95 @@ +//! Low level async timer driver. + +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use super::{Channel, GeneralInstance4Channel}; + +/// All timer interrupts +#[derive(Clone, Copy)] +pub enum InterruptFlag { + /// Update + Update = 0, + /// Capture/compare 1 + CaptureCompare1 = 1, + /// Capture/compare 2 + CaptureCompare2 = 2, + /// Capture/compare 3 + CaptureCompare3 = 3, + /// Capture/compare 4 + CaptureCompare4 = 4, + /// COM event + ComEvent = 5, + /// Trigger + Trigger = 6, + /// Break + Break = 7, +} + +/// Timer future +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct TimerEventFuture { + flag: InterruptFlag, + phantom: PhantomData, +} + +impl TimerEventFuture { + /// Enable the interrupt source and returns a new instance of Future + pub fn new(flag: InterruptFlag) -> Self { + let this = Self { + flag, + phantom: PhantomData, + }; + + this + } + + fn regs(&self) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } + } +} + +impl From for InterruptFlag { + fn from(value: Channel) -> Self { + match value { + Channel::Ch1 => InterruptFlag::CaptureCompare1, + Channel::Ch2 => InterruptFlag::CaptureCompare2, + Channel::Ch3 => InterruptFlag::CaptureCompare3, + Channel::Ch4 => InterruptFlag::CaptureCompare4, + } + } +} + +impl Drop for TimerEventFuture { + fn drop(&mut self) { + critical_section::with(|_| { + // clear interrupt enable + self.regs().dier().modify(|w| w.0 &= !(1u32 << self.flag as u32)); + }); + } +} + +impl Future for TimerEventFuture { + type Output = u32; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + T::state().wakers[self.flag as usize].register(cx.waker()); + + // if interrupt enable is cleared, this means the interrupt handler executed, thus we can return the value + let dier = self.regs().dier().read(); + if (dier.0 & (1u32 << self.flag as u32)) == 0 { + let val = match self.flag { + InterruptFlag::CaptureCompare1 => self.regs().ccr(Channel::Ch1.index()).read().0, + InterruptFlag::CaptureCompare2 => self.regs().ccr(Channel::Ch2.index()).read().0, + InterruptFlag::CaptureCompare3 => self.regs().ccr(Channel::Ch3.index()).read().0, + InterruptFlag::CaptureCompare4 => self.regs().ccr(Channel::Ch4.index()).read().0, + _ => self.regs().cnt().read().0, // return the counter value + }; + Poll::Ready(val) + } else { + Poll::Pending + } + } +} diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 97740c2ed6..9e39681dc8 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -8,6 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(stm32l0))] pub mod complementary_pwm; pub mod input_capture; +pub mod ll_async; pub mod low_level; pub mod pwm_input; pub mod qei; @@ -53,15 +54,13 @@ pub enum TimerBits { } struct State { - up_waker: AtomicWaker, - cc_waker: [AtomicWaker; 4], + wakers: [AtomicWaker; 8], } impl State { const fn new() -> Self { Self { - up_waker: AtomicWaker::new(), - cc_waker: [const { AtomicWaker::new() }; 4], + wakers: [const { AtomicWaker::new() }; 8], } } } @@ -320,12 +319,12 @@ foreach_interrupt! { }; } -/// Update interrupt handler. -pub struct UpdateInterruptHandler { +/// Global interrupt handler. +pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::typelevel::Handler for UpdateInterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { #[cfg(feature = "low-power")] crate::low_power::on_wakeup_irq(); @@ -335,46 +334,30 @@ impl interrupt::typelevel::Handler for Upda // Read TIM interrupt flags. let sr = regs.sr().read(); - // Mask relevant interrupts (UIE). - let bits = sr.0 & 0x00000001; + // Mask relevant interrupts (bits 0..7). + let bits = sr.0 & 0x000000FF; // Mask all the channels that fired. regs.dier().modify(|w| w.0 &= !bits); // Wake the tasks - if sr.uif() { - T::state().up_waker.wake(); + for pin in BitIter(bits) { + T::state().wakers[pin as usize].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()); +struct BitIter(u32); - // Read TIM interrupt flags. - let sr = regs.sr().read(); +impl Iterator for BitIter { + type Item = u32; - // 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(); + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) } } } diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index 5e2dab9e6c..8e21ad5e3f 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -29,7 +29,7 @@ async fn blinky(led: peripherals::PC13) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] @@ -44,9 +44,7 @@ async fn main(spawner: Spawner) { loop { info!("wait for rising edge"); - ic.wait_for_rising_edge(Channel::Ch3).await; - - let capture_value = ic.get_capture_value(Channel::Ch3); + let capture_value = ic.wait_for_rising_edge(Channel::Ch3).await; info!("new capture! {}", capture_value); } } diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index f74853d4ea..dfe9339912 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -28,7 +28,7 @@ async fn blinky(led: peripherals::PC13) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index 49de33d2b5..f125bbfabb 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs @@ -29,7 +29,7 @@ async fn blinky(led: peripherals::PB2) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index ce200549d5..03c9db2f90 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -28,7 +28,7 @@ async fn blinky(led: peripherals::PB2) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index bc814cb13c..fa576d42b4 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs @@ -36,7 +36,7 @@ async fn blinky(led: peripherals::PB1) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index db9cf4f8a1..9a2e61f88e 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -33,7 +33,7 @@ async fn blinky(led: peripherals::PB1) { } bind_interrupts!(struct Irqs { - TIM2 => timer::CaptureCompareInterruptHandler; + TIM2 => timer::InterruptHandler; }); #[embassy_executor::main] From 4b4f966e42731eee73e2b7bc423d1718acd7648c Mon Sep 17 00:00:00 2001 From: Hatim Thayyil Date: Thu, 5 Dec 2024 20:51:01 +0000 Subject: [PATCH 2/4] two timer channels as fields for PwmInput This is from brunob45's work on async timer interrupts. A second channel has been added to remove the need for the match in get_width_ticks. The channels have been named ch_period and ch_width (instead of ch1, and ch2) to prevent confusion with the timer channels Channel::Ch1 and Channel::Ch2. --- embassy-stm32/src/timer/pwm_input.rs | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index e3eb6042a9..b2417ed916 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -10,7 +10,8 @@ use crate::Peripheral; /// PWM Input driver. pub struct PwmInput<'d, T: GeneralInstance4Channel> { - channel: Channel, + ch_period: Channel, + ch_width: Channel, inner: Timer<'d, T>, } @@ -43,7 +44,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, ch_period: Channel, ch_width: Channel) -> Self { let mut inner = Timer::new(tim); inner.set_counting_mode(CountingMode::EdgeAlignedUp); @@ -53,13 +54,13 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode - inner.set_input_ti_selection(ch1, InputTISelection::Normal); - inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); + inner.set_input_ti_selection(ch_period, InputTISelection::Normal); + inner.set_input_capture_mode(ch_period, InputCaptureMode::Rising); - inner.set_input_ti_selection(ch2, InputTISelection::Alternate); - inner.set_input_capture_mode(ch2, InputCaptureMode::Falling); + inner.set_input_ti_selection(ch_width, InputTISelection::Alternate); + inner.set_input_capture_mode(ch_width, InputCaptureMode::Falling); - inner.set_trigger_source(match ch1 { + inner.set_trigger_source(match ch_period { Channel::Ch1 => TriggerSource::TI1FP1, Channel::Ch2 => TriggerSource::TI2FP2, _ => panic!("Invalid channel for PWM input"), @@ -69,7 +70,11 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { // Must call the `enable` function after - Self { channel: ch1, inner } + Self { + ch_period, + ch_width, + inner, + } } /// Enable the given channel. @@ -91,16 +96,12 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Get the period tick count pub fn get_period_ticks(&self) -> u32 { - self.inner.get_capture_value(self.channel) + self.inner.get_capture_value(self.ch_period) } /// Get the pulse width tick count pub fn get_width_ticks(&self) -> u32 { - self.inner.get_capture_value(match self.channel { - Channel::Ch1 => Channel::Ch2, - Channel::Ch2 => Channel::Ch1, - _ => panic!("Invalid channel for PWM input"), - }) + self.inner.get_capture_value(self.ch_width) } /// Get the duty cycle in 100% From 7ce433b5cc782414a7e213bb59cd4c5f82d49da5 Mon Sep 17 00:00:00 2001 From: Hatim Thayyil Date: Thu, 5 Dec 2024 21:03:22 +0000 Subject: [PATCH 3/4] add async methods to PwmInput This commit collects brunob45's work on async timers. Two methods are provided for asynchronously waiting on a PwmInput capture. --- embassy-stm32/src/timer/pwm_input.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index b2417ed916..02b5d2be00 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -2,9 +2,11 @@ use embassy_hal_internal::into_ref; +use super::ll_async::TimerEventFuture; use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; -use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; +use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel, InterruptHandler}; use crate::gpio::{AfType, Pull}; +use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; use crate::Peripheral; @@ -21,6 +23,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { tim: impl Peripheral

+ 'd, pin: impl Peripheral

> + 'd, pull: Pull, + _irq: impl Binding> + 'd, freq: Hertz, ) -> Self { into_ref!(pin); @@ -70,6 +73,9 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { // Must call the `enable` function after + T::CaptureCompareInterrupt::unpend(); + unsafe { T::CaptureCompareInterrupt::enable() }; + Self { ch_period, ch_width, @@ -112,4 +118,24 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { } 100. * (self.get_width_ticks() as f32) / (period as f32) } + + /// Asynchronously wait until the pin sees a rising edge (period measurement). + pub async fn wait_for_rising_edge(&self) -> u32 { + self.inner.clear_input_interrupt(self.ch_period); + self.inner.enable_input_interrupt(self.ch_period, true); + + // Rising edge is always on the main channel + let future: TimerEventFuture = TimerEventFuture::new(self.ch_period.into()); + future.await + } + + /// Asynchronously wait until the pin sees a falling edge (width measurement). + pub async fn wait_for_falling_edge(&self) -> u32 { + // Falling edge is always on the alternate channel + self.inner.clear_input_interrupt(self.ch_width); + self.inner.enable_input_interrupt(self.ch_width, true); + + let future: TimerEventFuture = TimerEventFuture::new(self.ch_width.into()); + future.await + } } From a420f5dd47c90c19368c318be104110df42e5a6a Mon Sep 17 00:00:00 2001 From: Hatim Thayyil Date: Thu, 5 Dec 2024 21:30:36 +0000 Subject: [PATCH 4/4] update examples to use async PwmInput --- examples/stm32f1/src/bin/pwm_input.rs | 6 +++--- examples/stm32f4/src/bin/pwm_input.rs | 8 ++++---- examples/stm32g0/src/bin/pwm_input.rs | 5 ++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index dfe9339912..f4ce86eacf 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -38,14 +38,14 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PC13))); - let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, Irqs, khz(10)); pwm_input.enable(); loop { - Timer::after_millis(500).await; - let period = pwm_input.get_period_ticks(); + let period = pwm_input.wait_for_rising_edge().await; let width = pwm_input.get_width_ticks(); let duty_cycle = pwm_input.get_duty_cycle(); + info!( "period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 03c9db2f90..a990e48468 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -28,7 +28,7 @@ async fn blinky(led: peripherals::PB2) { } bind_interrupts!(struct Irqs { - TIM2 => timer::InterruptHandler; + TIM3 => timer::InterruptHandler; }); #[embassy_executor::main] @@ -38,14 +38,14 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB2))); - let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, Irqs, khz(10)); pwm_input.enable(); loop { - Timer::after_millis(500).await; - let period = pwm_input.get_period_ticks(); + let period = pwm_input.wait_for_rising_edge().await; let width = pwm_input.get_width_ticks(); let duty_cycle = pwm_input.get_duty_cycle(); + info!( "period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 9a2e61f88e..43bc01e585 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -47,12 +47,11 @@ async fn main(spawner: Spawner) { pwm.ch1().set_duty_cycle_fraction(1, 4); pwm.ch1().enable(); - let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); + let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, Irqs, khz(1000)); pwm_input.enable(); loop { - Timer::after_millis(500).await; - let period = pwm_input.get_period_ticks(); + let period = pwm_input.wait_for_rising_edge().await; let width = pwm_input.get_width_ticks(); let duty_cycle = pwm_input.get_duty_cycle(); info!(