Merge pull request #2912 from brunob45/simple_capture
STM32 Input Capture
This commit is contained in:
		
						commit
						621dbeceda
					
				
							
								
								
									
										233
									
								
								embassy-stm32/src/timer/input_capture.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								embassy-stm32/src/timer/input_capture.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | ||||
| //! 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, 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; | ||||
| 
 | ||||
| /// Channel 1 marker type.
 | ||||
| pub enum Ch1 {} | ||||
| /// Channel 2 marker type.
 | ||||
| pub enum Ch2 {} | ||||
| /// Channel 3 marker type.
 | ||||
| pub enum Ch3 {} | ||||
| /// Channel 4 marker type.
 | ||||
| pub enum Ch4 {} | ||||
| 
 | ||||
| /// Capture pin wrapper.
 | ||||
| ///
 | ||||
| /// This wraps a pin to make it usable with capture.
 | ||||
| pub struct CapturePin<'d, T, C> { | ||||
|     _pin: PeripheralRef<'d, AnyPin>, | ||||
|     phantom: PhantomData<(T, C)>, | ||||
| } | ||||
| 
 | ||||
| macro_rules! channel_impl { | ||||
|     ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | ||||
|         impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { | ||||
|             #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] | ||||
|             pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull_type: Pull) -> Self { | ||||
|                 into_ref!(pin); | ||||
|                 critical_section::with(|_| { | ||||
|                     pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); | ||||
|                     #[cfg(gpio_v2)] | ||||
|                     pin.set_speed(crate::gpio::Speed::VeryHigh); | ||||
|                 }); | ||||
|                 CapturePin { | ||||
|                     _pin: pin.map_into(), | ||||
|                     phantom: PhantomData, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| channel_impl!(new_ch1, Ch1, Channel1Pin); | ||||
| channel_impl!(new_ch2, Ch2, Channel2Pin); | ||||
| channel_impl!(new_ch3, Ch3, Channel3Pin); | ||||
| channel_impl!(new_ch4, Ch4, Channel4Pin); | ||||
| 
 | ||||
| /// Input capture driver.
 | ||||
| pub struct InputCapture<'d, T: GeneralInstance4Channel> { | ||||
|     inner: Timer<'d, T>, | ||||
| } | ||||
| 
 | ||||
| impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | ||||
|     /// Create a new input capture driver.
 | ||||
|     pub fn new( | ||||
|         tim: impl Peripheral<P = T> + 'd, | ||||
|         _ch1: Option<CapturePin<'d, T, Ch1>>, | ||||
|         _ch2: Option<CapturePin<'d, T, Ch2>>, | ||||
|         _ch3: Option<CapturePin<'d, T, Ch3>>, | ||||
|         _ch4: Option<CapturePin<'d, T, Ch4>>, | ||||
|         _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||||
|         freq: Hertz, | ||||
|         counting_mode: CountingMode, | ||||
|     ) -> Self { | ||||
|         Self::new_inner(tim, freq, counting_mode) | ||||
|     } | ||||
| 
 | ||||
|     fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | ||||
|         let mut this = Self { inner: Timer::new(tim) }; | ||||
| 
 | ||||
|         this.inner.set_counting_mode(counting_mode); | ||||
|         this.set_tick_freq(freq); | ||||
|         this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
 | ||||
|         this.inner.start(); | ||||
| 
 | ||||
|         // enable NVIC interrupt
 | ||||
|         T::CaptureCompareInterrupt::unpend(); | ||||
|         unsafe { T::CaptureCompareInterrupt::enable() }; | ||||
| 
 | ||||
|         this | ||||
|     } | ||||
| 
 | ||||
|     /// Enable the given channel.
 | ||||
|     pub fn enable(&mut self, channel: Channel) { | ||||
|         self.inner.enable_channel(channel, true); | ||||
|     } | ||||
| 
 | ||||
|     /// Disable the given channel.
 | ||||
|     pub fn disable(&mut self, channel: Channel) { | ||||
|         self.inner.enable_channel(channel, false); | ||||
|     } | ||||
| 
 | ||||
|     /// Check whether given channel is enabled
 | ||||
|     pub fn is_enabled(&self, channel: Channel) -> bool { | ||||
|         self.inner.get_channel_enable_state(channel) | ||||
|     } | ||||
| 
 | ||||
|     /// Set tick frequency.
 | ||||
|     ///
 | ||||
|     /// Note: when you call this, the max period value changes
 | ||||
|     pub fn set_tick_freq(&mut self, freq: Hertz) { | ||||
|         let f = freq; | ||||
|         assert!(f.0 > 0); | ||||
|         let timer_f = self.inner.get_clock_frequency(); | ||||
| 
 | ||||
|         let pclk_ticks_per_timer_period = timer_f / f; | ||||
|         let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); | ||||
| 
 | ||||
|         let regs = self.inner.regs_core(); | ||||
|         regs.psc().write_value(psc); | ||||
| 
 | ||||
|         // Generate an Update Request
 | ||||
|         regs.egr().write(|r| r.set_ug(true)); | ||||
|     } | ||||
| 
 | ||||
|     /// Set the input capture mode for a given channel.
 | ||||
|     pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { | ||||
|         self.inner.set_input_capture_mode(channel, mode); | ||||
|     } | ||||
| 
 | ||||
|     /// Set input TI selection.
 | ||||
|     pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { | ||||
|         self.inner.set_input_ti_selection(channel, tisel) | ||||
|     } | ||||
| 
 | ||||
|     /// Get capture value for a channel.
 | ||||
|     pub fn get_capture_value(&self, channel: Channel) -> u32 { | ||||
|         self.inner.get_capture_value(channel) | ||||
|     } | ||||
| 
 | ||||
|     /// Get input interrupt.
 | ||||
|     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<T> { | ||||
|         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<T: GeneralInstance4Channel> { | ||||
|     channel: Channel, | ||||
|     phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> { | ||||
|     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<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> { | ||||
|     type Output = u32; | ||||
| 
 | ||||
|     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||
|         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 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -448,6 +448,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | ||||
|         self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); | ||||
|     } | ||||
| 
 | ||||
|     /// Get input interrupt.
 | ||||
|     pub fn get_input_interrupt(&self, channel: Channel) -> bool { | ||||
|         self.regs_gp16().sr().read().ccif(channel.index()) | ||||
|     } | ||||
| 
 | ||||
|     /// Enable input interrupt.
 | ||||
|     pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { | ||||
|         self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); | ||||
|  | ||||
| @ -1,7 +1,12 @@ | ||||
| //! Timers, PWM, quadrature decoder.
 | ||||
| 
 | ||||
| use core::marker::PhantomData; | ||||
| 
 | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
| 
 | ||||
| #[cfg(not(stm32l0))] | ||||
| pub mod complementary_pwm; | ||||
| pub mod input_capture; | ||||
| pub mod low_level; | ||||
| pub mod qei; | ||||
| pub mod simple_pwm; | ||||
| @ -45,8 +50,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; | ||||
| 
 | ||||
| @ -143,6 +169,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; | ||||
| 
 | ||||
| @ -285,3 +318,63 @@ foreach_interrupt! { | ||||
|         impl AdvancedInstance4Channel for crate::peripherals::$inst {} | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Update interrupt handler.
 | ||||
| pub struct UpdateInterruptHandler<T: CoreInstance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | ||||
|     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<T: GeneralInstance1Channel> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompareInterrupt> | ||||
|     for CaptureCompareInterruptHandler<T> | ||||
| { | ||||
|     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(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										52
									
								
								examples/stm32f4/src/bin/input_capture.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								examples/stm32f4/src/bin/input_capture.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| 
 | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | ||||
| use embassy_stm32::time::khz; | ||||
| use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | ||||
| use embassy_stm32::timer::{self, Channel}; | ||||
| use embassy_stm32::{bind_interrupts, peripherals}; | ||||
| 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: peripherals::PB2) { | ||||
|     let mut led = Output::new(led, Level::High, Speed::Low); | ||||
| 
 | ||||
|     loop { | ||||
|         info!("high"); | ||||
|         led.set_high(); | ||||
|         Timer::after_millis(300).await; | ||||
| 
 | ||||
|         info!("low"); | ||||
|         led.set_low(); | ||||
|         Timer::after_millis(300).await; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bind_interrupts!(struct Irqs { | ||||
|     TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>; | ||||
| }); | ||||
| 
 | ||||
| #[embassy_executor::main] | ||||
| async fn main(spawner: Spawner) { | ||||
|     let p = embassy_stm32::init(Default::default()); | ||||
|     info!("Hello World!"); | ||||
| 
 | ||||
|     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, Irqs, khz(1000), Default::default()); | ||||
| 
 | ||||
|     loop { | ||||
|         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); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user