stm32/timer: Support one pulse mode
Currently does not support output pins so it really is only useful to create delayed interrupts based on external signals.
This commit is contained in:
parent
eee2d8c84d
commit
f8e5c90266
@ -425,6 +425,36 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
|
||||
TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the max compare value.
|
||||
///
|
||||
/// An update event is generated to load the new value. The update event is
|
||||
/// generated such that it will not cause an interrupt or DMA request.
|
||||
pub fn set_max_compare_value(&self, ticks: u32) {
|
||||
match T::BITS {
|
||||
TimerBits::Bits16 => {
|
||||
let arr = unwrap!(u16::try_from(ticks));
|
||||
|
||||
let regs = self.regs_1ch();
|
||||
regs.arr().write(|r| r.set_arr(arr));
|
||||
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
|
||||
regs.egr().write(|r| r.set_ug(true));
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
|
||||
}
|
||||
#[cfg(not(stm32l0))]
|
||||
TimerBits::Bits32 => {
|
||||
let arr = ticks;
|
||||
|
||||
let regs = self.regs_gp32_unchecked();
|
||||
regs.arr().write_value(arr);
|
||||
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
|
||||
regs.egr().write(|r| r.set_ug(true));
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: GeneralInstance2Channel> Timer<'d, T> {
|
||||
|
||||
@ -9,6 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker;
|
||||
pub mod complementary_pwm;
|
||||
pub mod input_capture;
|
||||
pub mod low_level;
|
||||
pub mod one_pulse;
|
||||
pub mod pwm_input;
|
||||
pub mod qei;
|
||||
pub mod simple_pwm;
|
||||
|
||||
384
embassy-stm32/src/timer/one_pulse.rs
Normal file
384
embassy-stm32/src/timer/one_pulse.rs
Normal file
@ -0,0 +1,384 @@
|
||||
//! 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,
|
||||
};
|
||||
use super::{
|
||||
CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel,
|
||||
};
|
||||
pub use super::{Ch1, Ch2};
|
||||
use crate::gpio::{AfType, AnyPin, Pull};
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::pac::timer::vals::Etp;
|
||||
use crate::time::Hertz;
|
||||
use crate::{into_ref, Peripheral, PeripheralRef};
|
||||
|
||||
/// 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<ExternalTriggerPolarity> 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> {
|
||||
_pin: PeripheralRef<'d, AnyPin>,
|
||||
phantom: PhantomData<(T, C)>,
|
||||
}
|
||||
|
||||
macro_rules! channel_impl {
|
||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||
impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> {
|
||||
#[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")]
|
||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull: Pull) -> Self {
|
||||
into_ref!(pin);
|
||||
pin.set_as_af(pin.af_num(), AfType::input(pull));
|
||||
TriggerPin {
|
||||
_pin: pin.map_into(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
channel_impl!(new_ch1, Ch1, Channel1Pin);
|
||||
channel_impl!(new_ch2, Ch2, Channel2Pin);
|
||||
channel_impl!(new_ext, Ext, ExternalTriggerPin);
|
||||
|
||||
/// 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.
|
||||
pub fn new_ch1_edge_detect(
|
||||
tim: impl Peripheral<P = T> + 'd,
|
||||
_pin: TriggerPin<'d, T, Ch1>,
|
||||
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
|
||||
freq: Hertz,
|
||||
pulse_end: u32,
|
||||
counting_mode: CountingMode,
|
||||
) -> Self {
|
||||
let mut this = Self { inner: Timer::new(tim) };
|
||||
|
||||
this.inner.set_trigger_source(TriggerSource::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: impl Peripheral<P = T> + 'd,
|
||||
_pin: TriggerPin<'d, T, Ch1>,
|
||||
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + '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(TriggerSource::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: impl Peripheral<P = T> + 'd,
|
||||
_pin: TriggerPin<'d, T, Ch1>,
|
||||
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + '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(TriggerSource::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: impl Peripheral<P = T> + 'd,
|
||||
_pin: TriggerPin<'d, T, Ext>,
|
||||
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + '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(TriggerSource::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(pulse_end);
|
||||
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 = self.inner.get_max_compare_value();
|
||||
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(ticks)
|
||||
}
|
||||
|
||||
/// 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<Timer<'d, T>>,
|
||||
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 = self.inner.get_max_compare_value();
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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, delay);
|
||||
}
|
||||
|
||||
/// 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::<T> {
|
||||
channel: self.channel,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
struct OnePulseFuture<T: GeneralInstance4Channel> {
|
||||
channel: Channel,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'d, T: GeneralInstance4Channel> Drop for OnePulseFuture<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<'d, T: GeneralInstance4Channel> Future for OnePulseFuture<T> {
|
||||
type Output = ();
|
||||
|
||||
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()) {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user