//! One pulse mode driver. use core::future::Future; use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::pin::Pin; use core::task::{Context, Poll}; use super::low_level::{ CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts, }; use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; pub use super::{Ch1, Ch2}; use crate::Peri; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac::timer::vals::Etp; use crate::time::Hertz; use crate::timer::TimerChannel; /// External input marker type. pub enum Ext {} /// External trigger pin trigger polarity. #[derive(Clone, Copy)] pub enum ExternalTriggerPolarity { /// Rising edge only. Rising, /// Falling edge only. Falling, } impl From for Etp { fn from(mode: ExternalTriggerPolarity) -> Self { match mode { ExternalTriggerPolarity::Rising => 0.into(), ExternalTriggerPolarity::Falling => 1.into(), } } } /// Trigger pin wrapper. /// /// This wraps a pin to make it usable as a timer trigger. pub struct TriggerPin<'d, T, C> { #[allow(unused)] pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } trait SealedTriggerSource {} /// Marker trait for a trigger source. #[expect(private_bounds)] pub trait TriggerSource: SealedTriggerSource {} impl TriggerSource for Ch1 {} impl TriggerSource for Ch2 {} impl TriggerSource for Ext {} impl SealedTriggerSource for Ch1 {} impl SealedTriggerSource for Ch2 {} impl SealedTriggerSource for Ext {} impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> { /// Create a new Channel trigger pin instance. pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin)>, pull: Pull) -> Self { set_as_af!(pin, AfType::input(pull)); TriggerPin { pin: pin.into(), phantom: PhantomData, } } } impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { /// Create a new external trigger pin instance. pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, pull: Pull) -> Self { set_as_af!(pin, AfType::input(pull)); TriggerPin { pin: pin.into(), phantom: PhantomData, } } } /// One pulse driver. /// /// Generates a pulse after a trigger and some configurable delay. pub struct OnePulse<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, } impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// Create a new one pulse driver. /// /// The pulse is triggered by a channel 1 input pin on both rising and /// falling edges. Channel 1 will unusable as an output. #[allow(unused)] pub fn new_ch1_edge_detect( tim: Peri<'d, T>, pin: TriggerPin<'d, T, Ch1>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, counting_mode: CountingMode, ) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_trigger_source(Ts::TI1F_ED); this.inner .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); this.inner .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); this.new_inner(freq, pulse_end, counting_mode); this } /// Create a new one pulse driver. /// /// The pulse is triggered by a channel 1 input pin. Channel 1 will unusable /// as an output. pub fn new_ch1( tim: Peri<'d, T>, _pin: TriggerPin<'d, T, Ch1>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, counting_mode: CountingMode, capture_mode: InputCaptureMode, ) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_trigger_source(Ts::TI1FP1); this.inner .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); this.inner .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); this.inner.set_input_capture_mode(Channel::Ch1, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this } /// Create a new one pulse driver. /// /// The pulse is triggered by a channel 2 input pin. Channel 2 will unusable /// as an output. pub fn new_ch2( tim: Peri<'d, T>, _pin: TriggerPin<'d, T, Ch2>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, counting_mode: CountingMode, capture_mode: InputCaptureMode, ) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_trigger_source(Ts::TI2FP2); this.inner .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); this.inner .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER); this.inner.set_input_capture_mode(Channel::Ch2, capture_mode); this.new_inner(freq, pulse_end, counting_mode); this } /// Create a new one pulse driver. /// /// The pulse is triggered by a external trigger input pin. pub fn new_ext( tim: Peri<'d, T>, _pin: TriggerPin<'d, T, Ext>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, counting_mode: CountingMode, polarity: ExternalTriggerPolarity, ) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.regs_gp16().smcr().modify(|r| { r.set_etp(polarity.into()); // No pre-scaling r.set_etps(0.into()); // No filtering r.set_etf(FilterValue::NO_FILTER); }); this.inner.set_trigger_source(Ts::ETRF); this.new_inner(freq, pulse_end, counting_mode); this } fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { self.inner.set_counting_mode(counting_mode); self.inner.set_tick_freq(freq); self.inner.set_max_compare_value(unwrap!(pulse_end.try_into())); self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); // Required for advanced timers, see GeneralInstance4Channel for details self.inner.enable_outputs(); self.inner.set_slave_mode(SlaveMode::TRIGGER_MODE); T::CaptureCompareInterrupt::unpend(); unsafe { T::CaptureCompareInterrupt::enable() }; } /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } /// Set the end of the pulse in ticks from the trigger. pub fn set_pulse_end(&mut self, ticks: u32) { self.inner.set_max_compare_value(unwrap!(ticks.try_into())) } /// Reset the timer on each trigger #[cfg(not(stm32l0))] pub fn set_reset_on_trigger(&mut self, reset: bool) { let slave_mode = if reset { SlaveMode::COMBINED_RESET_TRIGGER } else { SlaveMode::TRIGGER_MODE }; self.inner.set_slave_mode(slave_mode); } /// Get a single channel /// /// If you need to use multiple channels, use [`Self::split`]. pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> { OnePulseChannel { inner: unsafe { self.inner.clone_unchecked() }, channel, } } /// Channel 1 /// /// This is just a convenience wrapper around [`Self::channel`]. /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch1(&mut self) -> OnePulseChannel<'_, T> { self.channel(Channel::Ch1) } /// Channel 2 /// /// This is just a convenience wrapper around [`Self::channel`]. /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch2(&mut self) -> OnePulseChannel<'_, T> { self.channel(Channel::Ch2) } /// Channel 3 /// /// This is just a convenience wrapper around [`Self::channel`]. /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch3(&mut self) -> OnePulseChannel<'_, T> { self.channel(Channel::Ch3) } /// Channel 4 /// /// This is just a convenience wrapper around [`Self::channel`]. /// /// If you need to use multiple channels, use [`Self::split`]. pub fn ch4(&mut self) -> OnePulseChannel<'_, T> { self.channel(Channel::Ch4) } /// Splits a [`OnePulse`] into four output channels. // TODO: I hate the name "split" pub fn split(self) -> OnePulseChannels<'static, T> where // must be static because the timer will never be dropped/disabled 'd: 'static, { // without this, the timer would be disabled at the end of this function let timer = ManuallyDrop::new(self.inner); let ch = |channel| OnePulseChannel { inner: unsafe { timer.clone_unchecked() }, channel, }; OnePulseChannels { ch1: ch(Channel::Ch1), ch2: ch(Channel::Ch2), ch3: ch(Channel::Ch3), ch4: ch(Channel::Ch4), } } } /// A group of four [`OnePulseChannel`]s, obtained from [`OnePulse::split`]. pub struct OnePulseChannels<'d, T: GeneralInstance4Channel> { /// Channel 1 pub ch1: OnePulseChannel<'d, T>, /// Channel 2 pub ch2: OnePulseChannel<'d, T>, /// Channel 3 pub ch3: OnePulseChannel<'d, T>, /// Channel 4 pub ch4: OnePulseChannel<'d, T>, } /// A single channel of a one pulse-configured timer, obtained from /// [`OnePulse::split`],[`OnePulse::channel`], [`OnePulse::ch1`], etc. /// /// It is not possible to change the pulse end tick because the end tick /// configuration is shared with all four channels. pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { inner: ManuallyDrop>, channel: Channel, } impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } /// Get the width of the pulse in ticks. pub fn pulse_width(&mut self) -> u32 { self.pulse_end().saturating_sub(self.pulse_delay()) } /// Get the start of the pulse in ticks from the trigger. pub fn pulse_delay(&mut self) -> u32 { self.inner.get_compare_value(self.channel).into() } /// Set the start of the pulse in ticks from the trigger. pub fn set_pulse_delay(&mut self, delay: u32) { assert!(delay <= self.pulse_end()); self.inner.set_compare_value(self.channel, unwrap!(delay.try_into())); } /// Set the pulse width in ticks. pub fn set_pulse_width(&mut self, width: u32) { assert!(width <= self.pulse_end()); self.set_pulse_delay(self.pulse_end() - width); } /// Waits until the trigger and following delay has passed. pub async fn wait_for_pulse_start(&mut self) { self.inner.enable_input_interrupt(self.channel, true); OnePulseFuture:: { channel: self.channel, phantom: PhantomData, } .await } } #[must_use = "futures do nothing unless you `.await` or poll them"] struct OnePulseFuture { channel: Channel, phantom: PhantomData, } impl<'d, T: GeneralInstance4Channel> Drop for OnePulseFuture { 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<'d, T: GeneralInstance4Channel> Future for OnePulseFuture { type Output = (); 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()) { Poll::Ready(()) } else { Poll::Pending } } }