diff options
| author | Bruno Bousquet <[email protected]> | 2024-05-06 02:47:42 -0400 |
|---|---|---|
| committer | Bruno Bousquet <[email protected]> | 2024-05-06 02:47:42 -0400 |
| commit | 55c8d3f4743bc653c1659dc3e961df86afe7d3db (patch) | |
| tree | 3ec6a5afaf16d4a7aa38fd532a661e597a832acd | |
| parent | b662dfb1838a72d243e939da9349c68c8fef5bdc (diff) | |
add async capture
| -rw-r--r-- | embassy-stm32/src/timer/input_capture.rs | 100 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 93 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/input_capture.rs | 26 |
3 files changed, 199 insertions, 20 deletions
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 @@ | |||
| 1 | //! Input capture driver. | 1 | //! Input capture driver. |
| 2 | 2 | ||
| 3 | use core::future::Future; | ||
| 3 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::pin::Pin; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 4 | 7 | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 6 | 9 | ||
| 7 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; | 10 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; |
| 11 | use super::CaptureCompareInterruptHandler; | ||
| 8 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; | 12 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; |
| 9 | use crate::gpio::{AFType, AnyPin, Pull}; | 13 | use crate::gpio::{AFType, AnyPin, Pull}; |
| 14 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 10 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 11 | use crate::Peripheral; | 16 | use crate::Peripheral; |
| 12 | 17 | ||
| @@ -65,6 +70,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 65 | _ch2: Option<CapturePin<'d, T, Ch2>>, | 70 | _ch2: Option<CapturePin<'d, T, Ch2>>, |
| 66 | _ch3: Option<CapturePin<'d, T, Ch3>>, | 71 | _ch3: Option<CapturePin<'d, T, Ch3>>, |
| 67 | _ch4: Option<CapturePin<'d, T, Ch4>>, | 72 | _ch4: Option<CapturePin<'d, T, Ch4>>, |
| 73 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||
| 68 | freq: Hertz, | 74 | freq: Hertz, |
| 69 | counting_mode: CountingMode, | 75 | counting_mode: CountingMode, |
| 70 | ) -> Self { | 76 | ) -> Self { |
| @@ -79,13 +85,9 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 79 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 85 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 80 | this.inner.start(); | 86 | this.inner.start(); |
| 81 | 87 | ||
| 82 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 88 | // enable NVIC interrupt |
| 83 | .iter() | 89 | T::CaptureCompareInterrupt::unpend(); |
| 84 | .for_each(|&channel| { | 90 | unsafe { T::CaptureCompareInterrupt::enable() }; |
| 85 | this.inner.set_input_capture_mode(channel, InputCaptureMode::Rising); | ||
| 86 | |||
| 87 | this.inner.set_input_ti_selection(channel, InputTISelection::Normal); | ||
| 88 | }); | ||
| 89 | 91 | ||
| 90 | this | 92 | this |
| 91 | } | 93 | } |
| @@ -142,4 +144,88 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 142 | pub fn get_input_interrupt(&self, channel: Channel) -> bool { | 144 | pub fn get_input_interrupt(&self, channel: Channel) -> bool { |
| 143 | self.inner.get_input_interrupt(channel) | 145 | self.inner.get_input_interrupt(channel) |
| 144 | } | 146 | } |
| 147 | |||
| 148 | fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> { | ||
| 149 | self.inner.enable_channel(channel, true); | ||
| 150 | self.inner.set_input_capture_mode(channel, mode); | ||
| 151 | self.inner.set_input_ti_selection(channel, tisel); | ||
| 152 | self.inner.clear_input_interrupt(channel); | ||
| 153 | self.inner.enable_input_interrupt(channel, true); | ||
| 154 | |||
| 155 | InputCaptureFuture { | ||
| 156 | channel, | ||
| 157 | phantom: PhantomData, | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Asynchronously wait until the pin sees a rising edge. | ||
| 162 | pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> u32 { | ||
| 163 | self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal) | ||
| 164 | .await | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Asynchronously wait until the pin sees a falling edge. | ||
| 168 | pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> u32 { | ||
| 169 | self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal) | ||
| 170 | .await | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Asynchronously wait until the pin sees any edge. | ||
| 174 | pub async fn wait_for_any_edge(&mut self, channel: Channel) -> u32 { | ||
| 175 | self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal) | ||
| 176 | .await | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Asynchronously wait until the (alternate) pin sees a rising edge. | ||
| 180 | pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> u32 { | ||
| 181 | self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate) | ||
| 182 | .await | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Asynchronously wait until the (alternate) pin sees a falling edge. | ||
| 186 | pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> u32 { | ||
| 187 | self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate) | ||
| 188 | .await | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Asynchronously wait until the (alternate) pin sees any edge. | ||
| 192 | pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> u32 { | ||
| 193 | self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate) | ||
| 194 | .await | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 199 | struct InputCaptureFuture<T: GeneralInstance4Channel> { | ||
| 200 | channel: Channel, | ||
| 201 | phantom: PhantomData<T>, | ||
| 202 | } | ||
| 203 | |||
| 204 | impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> { | ||
| 205 | fn drop(&mut self) { | ||
| 206 | critical_section::with(|_| { | ||
| 207 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | ||
| 208 | |||
| 209 | // disable interrupt enable | ||
| 210 | regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); | ||
| 211 | }); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | impl<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> { | ||
| 216 | type Output = u32; | ||
| 217 | |||
| 218 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 219 | T::state().cc_waker[self.channel.index()].register(cx.waker()); | ||
| 220 | |||
| 221 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | ||
| 222 | |||
| 223 | let dier = regs.dier().read(); | ||
| 224 | if !dier.ccie(self.channel.index()) { | ||
| 225 | let val = regs.ccr(self.channel.index()).read().0; | ||
| 226 | Poll::Ready(val) | ||
| 227 | } else { | ||
| 228 | Poll::Pending | ||
| 229 | } | ||
| 230 | } | ||
| 145 | } | 231 | } |
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 @@ | |||
| 1 | //! Timers, PWM, quadrature decoder. | 1 | //! Timers, PWM, quadrature decoder. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 5 | |||
| 3 | #[cfg(not(stm32l0))] | 6 | #[cfg(not(stm32l0))] |
| 4 | pub mod complementary_pwm; | 7 | pub mod complementary_pwm; |
| 5 | pub mod input_capture; | 8 | pub mod input_capture; |
| @@ -46,8 +49,29 @@ pub enum TimerBits { | |||
| 46 | Bits32, | 49 | Bits32, |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 52 | struct State { | ||
| 53 | up_waker: AtomicWaker, | ||
| 54 | cc_waker: [AtomicWaker; 4], | ||
| 55 | } | ||
| 56 | |||
| 57 | impl State { | ||
| 58 | const fn new() -> Self { | ||
| 59 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 60 | Self { | ||
| 61 | up_waker: NEW_AW, | ||
| 62 | cc_waker: [NEW_AW; 4], | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | trait SealedInstance: RccPeripheral { | ||
| 68 | /// Async state for this timer | ||
| 69 | fn state() -> &'static State; | ||
| 70 | } | ||
| 71 | |||
| 49 | /// Core timer instance. | 72 | /// Core timer instance. |
| 50 | pub trait CoreInstance: RccPeripheral + 'static { | 73 | #[allow(private_bounds)] |
| 74 | pub trait CoreInstance: SealedInstance + 'static { | ||
| 51 | /// Update Interrupt for this timer. | 75 | /// Update Interrupt for this timer. |
| 52 | type UpdateInterrupt: interrupt::typelevel::Interrupt; | 76 | type UpdateInterrupt: interrupt::typelevel::Interrupt; |
| 53 | 77 | ||
| @@ -144,6 +168,13 @@ dma_trait!(Ch4Dma, GeneralInstance4Channel); | |||
| 144 | #[allow(unused)] | 168 | #[allow(unused)] |
| 145 | macro_rules! impl_core_timer { | 169 | macro_rules! impl_core_timer { |
| 146 | ($inst:ident, $bits:expr) => { | 170 | ($inst:ident, $bits:expr) => { |
| 171 | impl SealedInstance for crate::peripherals::$inst { | ||
| 172 | fn state() -> &'static State { | ||
| 173 | static STATE: State = State::new(); | ||
| 174 | &STATE | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 147 | impl CoreInstance for crate::peripherals::$inst { | 178 | impl CoreInstance for crate::peripherals::$inst { |
| 148 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; | 179 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; |
| 149 | 180 | ||
| @@ -286,3 +317,63 @@ foreach_interrupt! { | |||
| 286 | impl AdvancedInstance4Channel for crate::peripherals::$inst {} | 317 | impl AdvancedInstance4Channel for crate::peripherals::$inst {} |
| 287 | }; | 318 | }; |
| 288 | } | 319 | } |
| 320 | |||
| 321 | /// Update interrupt handler. | ||
| 322 | pub struct UpdateInterruptHandler<T: CoreInstance> { | ||
| 323 | _phantom: PhantomData<T>, | ||
| 324 | } | ||
| 325 | |||
| 326 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | ||
| 327 | unsafe fn on_interrupt() { | ||
| 328 | #[cfg(feature = "low-power")] | ||
| 329 | crate::low_power::on_wakeup_irq(); | ||
| 330 | |||
| 331 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | ||
| 332 | |||
| 333 | // Read TIM interrupt flags. | ||
| 334 | let sr = regs.sr().read(); | ||
| 335 | |||
| 336 | // Mask relevant interrupts (UIE). | ||
| 337 | let bits = sr.0 & 0x00000001; | ||
| 338 | |||
| 339 | // Mask all the channels that fired. | ||
| 340 | regs.dier().modify(|w| w.0 &= !bits); | ||
| 341 | |||
| 342 | // Wake the tasks | ||
| 343 | if sr.uif() { | ||
| 344 | T::state().up_waker.wake(); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Capture/Compare interrupt handler. | ||
| 350 | pub struct CaptureCompareInterruptHandler<T: GeneralInstance1Channel> { | ||
| 351 | _phantom: PhantomData<T>, | ||
| 352 | } | ||
| 353 | |||
| 354 | impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompareInterrupt> | ||
| 355 | for CaptureCompareInterruptHandler<T> | ||
| 356 | { | ||
| 357 | unsafe fn on_interrupt() { | ||
| 358 | #[cfg(feature = "low-power")] | ||
| 359 | crate::low_power::on_wakeup_irq(); | ||
| 360 | |||
| 361 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | ||
| 362 | |||
| 363 | // Read TIM interrupt flags. | ||
| 364 | let sr = regs.sr().read(); | ||
| 365 | |||
| 366 | // Mask relevant interrupts (CCIE). | ||
| 367 | let bits = sr.0 & 0x0000001E; | ||
| 368 | |||
| 369 | // Mask all the channels that fired. | ||
| 370 | regs.dier().modify(|w| w.0 &= !bits); | ||
| 371 | |||
| 372 | // Wake the tasks | ||
| 373 | for ch in 0..4 { | ||
| 374 | if sr.ccif(ch) { | ||
| 375 | T::state().cc_waker[ch].wake(); | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
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 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::bind_interrupts; | ||
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::peripherals::PB2; | 8 | use embassy_stm32::peripherals; |
| 8 | use embassy_stm32::time::khz; | 9 | use embassy_stm32::time::khz; |
| 9 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | 10 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; |
| 10 | use embassy_stm32::timer::Channel; | 11 | use embassy_stm32::timer::{self, Channel}; |
| 11 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 14 | /// Connect PB2 and PB10 with a 1k Ohm resistor | 15 | /// Connect PB2 and PB10 with a 1k Ohm resistor |
| 15 | 16 | ||
| 16 | #[embassy_executor::task] | 17 | #[embassy_executor::task] |
| 17 | async fn blinky(led: PB2) { | 18 | async fn blinky(led: peripherals::PB2) { |
| 18 | let mut led = Output::new(led, Level::High, Speed::Low); | 19 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 19 | 20 | ||
| 20 | loop { | 21 | loop { |
| @@ -28,6 +29,10 @@ async fn blinky(led: PB2) { | |||
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 32 | bind_interrupts!(struct Irqs { | ||
| 33 | TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>; | ||
| 34 | }); | ||
| 35 | |||
| 31 | #[embassy_executor::main] | 36 | #[embassy_executor::main] |
| 32 | async fn main(spawner: Spawner) { | 37 | async fn main(spawner: Spawner) { |
| 33 | let p = embassy_stm32::init(Default::default()); | 38 | let p = embassy_stm32::init(Default::default()); |
| @@ -36,16 +41,13 @@ async fn main(spawner: Spawner) { | |||
| 36 | unwrap!(spawner.spawn(blinky(p.PB2))); | 41 | unwrap!(spawner.spawn(blinky(p.PB2))); |
| 37 | 42 | ||
| 38 | let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); | 43 | let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); |
| 39 | let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, khz(1000), Default::default()); | 44 | let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); |
| 40 | ic.enable(Channel::Ch3); | ||
| 41 | 45 | ||
| 42 | loop { | 46 | loop { |
| 43 | // Check for input capture | 47 | info!("wait for risign edge"); |
| 44 | if ic.get_input_interrupt(Channel::Ch3) { | 48 | ic.wait_for_rising_edge(Channel::Ch3).await; |
| 45 | let capture_value = ic.get_capture_value(Channel::Ch3); | 49 | |
| 46 | info!("New capture! {}", capture_value); | 50 | let capture_value = ic.get_capture_value(Channel::Ch3); |
| 47 | } | 51 | info!("new capture! {}", capture_value); |
| 48 | // Wait a little bit | ||
| 49 | Timer::after_millis(1).await; | ||
| 50 | } | 52 | } |
| 51 | } | 53 | } |
