aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer
diff options
context:
space:
mode:
authorGabriel Smith <[email protected]>2025-04-02 18:30:06 +0000
committerGabriel Smith <[email protected]>2025-04-04 17:40:22 +0000
commitf8e5c902665edf02f78fbb223ce14b3743fc543b (patch)
tree5c79ed00031deb8b2159e208ab4d90542803df34 /embassy-stm32/src/timer
parenteee2d8c84d318b36a80759aad26e2303965c0565 (diff)
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.
Diffstat (limited to 'embassy-stm32/src/timer')
-rw-r--r--embassy-stm32/src/timer/low_level.rs30
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/one_pulse.rs384
3 files changed, 415 insertions, 0 deletions
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 5b0c95109..524c64c14 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -425,6 +425,36 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
425 TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), 425 TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
426 } 426 }
427 } 427 }
428
429 /// Set the max compare value.
430 ///
431 /// An update event is generated to load the new value. The update event is
432 /// generated such that it will not cause an interrupt or DMA request.
433 pub fn set_max_compare_value(&self, ticks: u32) {
434 match T::BITS {
435 TimerBits::Bits16 => {
436 let arr = unwrap!(u16::try_from(ticks));
437
438 let regs = self.regs_1ch();
439 regs.arr().write(|r| r.set_arr(arr));
440
441 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
442 regs.egr().write(|r| r.set_ug(true));
443 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
444 }
445 #[cfg(not(stm32l0))]
446 TimerBits::Bits32 => {
447 let arr = ticks;
448
449 let regs = self.regs_gp32_unchecked();
450 regs.arr().write_value(arr);
451
452 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
453 regs.egr().write(|r| r.set_ug(true));
454 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
455 }
456 }
457 }
428} 458}
429 459
430impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { 460impl<'d, T: GeneralInstance2Channel> Timer<'d, T> {
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 645556e8c..4a5aa5e00 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -9,6 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker;
9pub mod complementary_pwm; 9pub mod complementary_pwm;
10pub mod input_capture; 10pub mod input_capture;
11pub mod low_level; 11pub mod low_level;
12pub mod one_pulse;
12pub mod pwm_input; 13pub mod pwm_input;
13pub mod qei; 14pub mod qei;
14pub mod simple_pwm; 15pub mod simple_pwm;
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs
new file mode 100644
index 000000000..43ba3dedf
--- /dev/null
+++ b/embassy-stm32/src/timer/one_pulse.rs
@@ -0,0 +1,384 @@
1//! One pulse mode driver.
2
3use core::future::Future;
4use core::marker::PhantomData;
5use core::mem::ManuallyDrop;
6use core::pin::Pin;
7use core::task::{Context, Poll};
8
9use super::low_level::{
10 CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource,
11};
12use super::{
13 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel,
14};
15pub use super::{Ch1, Ch2};
16use crate::gpio::{AfType, AnyPin, Pull};
17use crate::interrupt::typelevel::{Binding, Interrupt};
18use crate::pac::timer::vals::Etp;
19use crate::time::Hertz;
20use crate::{into_ref, Peripheral, PeripheralRef};
21
22/// External input marker type.
23pub enum Ext {}
24
25/// External trigger pin trigger polarity.
26#[derive(Clone, Copy)]
27pub enum ExternalTriggerPolarity {
28 /// Rising edge only.
29 Rising,
30 /// Falling edge only.
31 Falling,
32}
33
34impl From<ExternalTriggerPolarity> for Etp {
35 fn from(mode: ExternalTriggerPolarity) -> Self {
36 match mode {
37 ExternalTriggerPolarity::Rising => 0.into(),
38 ExternalTriggerPolarity::Falling => 1.into(),
39 }
40 }
41}
42
43/// Trigger pin wrapper.
44///
45/// This wraps a pin to make it usable as a timer trigger.
46pub struct TriggerPin<'d, T, C> {
47 _pin: PeripheralRef<'d, AnyPin>,
48 phantom: PhantomData<(T, C)>,
49}
50
51macro_rules! channel_impl {
52 ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
53 impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> {
54 #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")]
55 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull: Pull) -> Self {
56 into_ref!(pin);
57 pin.set_as_af(pin.af_num(), AfType::input(pull));
58 TriggerPin {
59 _pin: pin.map_into(),
60 phantom: PhantomData,
61 }
62 }
63 }
64 };
65}
66
67channel_impl!(new_ch1, Ch1, Channel1Pin);
68channel_impl!(new_ch2, Ch2, Channel2Pin);
69channel_impl!(new_ext, Ext, ExternalTriggerPin);
70
71/// One pulse driver.
72///
73/// Generates a pulse after a trigger and some configurable delay.
74pub struct OnePulse<'d, T: GeneralInstance4Channel> {
75 inner: Timer<'d, T>,
76}
77
78impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
79 /// Create a new one pulse driver.
80 ///
81 /// The pulse is triggered by a channel 1 input pin on both rising and
82 /// falling edges. Channel 1 will unusable as an output.
83 pub fn new_ch1_edge_detect(
84 tim: impl Peripheral<P = T> + 'd,
85 _pin: TriggerPin<'d, T, Ch1>,
86 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
87 freq: Hertz,
88 pulse_end: u32,
89 counting_mode: CountingMode,
90 ) -> Self {
91 let mut this = Self { inner: Timer::new(tim) };
92
93 this.inner.set_trigger_source(TriggerSource::TI1F_ED);
94 this.inner
95 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
96 this.inner
97 .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER);
98 this.new_inner(freq, pulse_end, counting_mode);
99
100 this
101 }
102
103 /// Create a new one pulse driver.
104 ///
105 /// The pulse is triggered by a channel 1 input pin. Channel 1 will unusable
106 /// as an output.
107 pub fn new_ch1(
108 tim: impl Peripheral<P = T> + 'd,
109 _pin: TriggerPin<'d, T, Ch1>,
110 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
111 freq: Hertz,
112 pulse_end: u32,
113 counting_mode: CountingMode,
114 capture_mode: InputCaptureMode,
115 ) -> Self {
116 let mut this = Self { inner: Timer::new(tim) };
117
118 this.inner.set_trigger_source(TriggerSource::TI1FP1);
119 this.inner
120 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
121 this.inner
122 .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER);
123 this.inner.set_input_capture_mode(Channel::Ch1, capture_mode);
124 this.new_inner(freq, pulse_end, counting_mode);
125
126 this
127 }
128
129 /// Create a new one pulse driver.
130 ///
131 /// The pulse is triggered by a channel 2 input pin. Channel 2 will unusable
132 /// as an output.
133 pub fn new_ch2(
134 tim: impl Peripheral<P = T> + 'd,
135 _pin: TriggerPin<'d, T, Ch1>,
136 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
137 freq: Hertz,
138 pulse_end: u32,
139 counting_mode: CountingMode,
140 capture_mode: InputCaptureMode,
141 ) -> Self {
142 let mut this = Self { inner: Timer::new(tim) };
143
144 this.inner.set_trigger_source(TriggerSource::TI2FP2);
145 this.inner
146 .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal);
147 this.inner
148 .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER);
149 this.inner.set_input_capture_mode(Channel::Ch2, capture_mode);
150 this.new_inner(freq, pulse_end, counting_mode);
151
152 this
153 }
154
155 /// Create a new one pulse driver.
156 ///
157 /// The pulse is triggered by a external trigger input pin.
158 pub fn new_ext(
159 tim: impl Peripheral<P = T> + 'd,
160 _pin: TriggerPin<'d, T, Ext>,
161 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
162 freq: Hertz,
163 pulse_end: u32,
164 counting_mode: CountingMode,
165 polarity: ExternalTriggerPolarity,
166 ) -> Self {
167 let mut this = Self { inner: Timer::new(tim) };
168
169 this.inner.regs_gp16().smcr().modify(|r| {
170 r.set_etp(polarity.into());
171 // No pre-scaling
172 r.set_etps(0.into());
173 // No filtering
174 r.set_etf(FilterValue::NO_FILTER);
175 });
176 this.inner.set_trigger_source(TriggerSource::ETRF);
177 this.new_inner(freq, pulse_end, counting_mode);
178
179 this
180 }
181
182 fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) {
183 self.inner.set_counting_mode(counting_mode);
184 self.inner.set_tick_freq(freq);
185 self.inner.set_max_compare_value(pulse_end);
186 self.inner.regs_core().cr1().modify(|r| r.set_opm(true));
187 // Required for advanced timers, see GeneralInstance4Channel for details
188 self.inner.enable_outputs();
189 self.inner.set_slave_mode(SlaveMode::TRIGGER_MODE);
190
191 T::CaptureCompareInterrupt::unpend();
192 unsafe { T::CaptureCompareInterrupt::enable() };
193 }
194
195 /// Get the end of the pulse in ticks from the trigger.
196 pub fn pulse_end(&self) -> u32 {
197 let max = self.inner.get_max_compare_value();
198 assert!(max < u32::MAX);
199 max + 1
200 }
201
202 /// Set the end of the pulse in ticks from the trigger.
203 pub fn set_pulse_end(&mut self, ticks: u32) {
204 self.inner.set_max_compare_value(ticks)
205 }
206
207 /// Reset the timer on each trigger
208 #[cfg(not(stm32l0))]
209 pub fn set_reset_on_trigger(&mut self, reset: bool) {
210 let slave_mode = if reset {
211 SlaveMode::COMBINED_RESET_TRIGGER
212 } else {
213 SlaveMode::TRIGGER_MODE
214 };
215 self.inner.set_slave_mode(slave_mode);
216 }
217
218 /// Get a single channel
219 ///
220 /// If you need to use multiple channels, use [`Self::split`].
221 pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> {
222 OnePulseChannel {
223 inner: unsafe { self.inner.clone_unchecked() },
224 channel,
225 }
226 }
227
228 /// Channel 1
229 ///
230 /// This is just a convenience wrapper around [`Self::channel`].
231 ///
232 /// If you need to use multiple channels, use [`Self::split`].
233 pub fn ch1(&mut self) -> OnePulseChannel<'_, T> {
234 self.channel(Channel::Ch1)
235 }
236
237 /// Channel 2
238 ///
239 /// This is just a convenience wrapper around [`Self::channel`].
240 ///
241 /// If you need to use multiple channels, use [`Self::split`].
242 pub fn ch2(&mut self) -> OnePulseChannel<'_, T> {
243 self.channel(Channel::Ch2)
244 }
245
246 /// Channel 3
247 ///
248 /// This is just a convenience wrapper around [`Self::channel`].
249 ///
250 /// If you need to use multiple channels, use [`Self::split`].
251 pub fn ch3(&mut self) -> OnePulseChannel<'_, T> {
252 self.channel(Channel::Ch3)
253 }
254
255 /// Channel 4
256 ///
257 /// This is just a convenience wrapper around [`Self::channel`].
258 ///
259 /// If you need to use multiple channels, use [`Self::split`].
260 pub fn ch4(&mut self) -> OnePulseChannel<'_, T> {
261 self.channel(Channel::Ch4)
262 }
263
264 /// Splits a [`OnePulse`] into four output channels.
265 // TODO: I hate the name "split"
266 pub fn split(self) -> OnePulseChannels<'static, T>
267 where
268 // must be static because the timer will never be dropped/disabled
269 'd: 'static,
270 {
271 // without this, the timer would be disabled at the end of this function
272 let timer = ManuallyDrop::new(self.inner);
273
274 let ch = |channel| OnePulseChannel {
275 inner: unsafe { timer.clone_unchecked() },
276 channel,
277 };
278
279 OnePulseChannels {
280 ch1: ch(Channel::Ch1),
281 ch2: ch(Channel::Ch2),
282 ch3: ch(Channel::Ch3),
283 ch4: ch(Channel::Ch4),
284 }
285 }
286}
287
288/// A group of four [`OnePulseChannel`]s, obtained from [`OnePulse::split`].
289pub struct OnePulseChannels<'d, T: GeneralInstance4Channel> {
290 /// Channel 1
291 pub ch1: OnePulseChannel<'d, T>,
292 /// Channel 2
293 pub ch2: OnePulseChannel<'d, T>,
294 /// Channel 3
295 pub ch3: OnePulseChannel<'d, T>,
296 /// Channel 4
297 pub ch4: OnePulseChannel<'d, T>,
298}
299
300/// A single channel of a one pulse-configured timer, obtained from
301/// [`OnePulse::split`],[`OnePulse::channel`], [`OnePulse::ch1`], etc.
302///
303/// It is not possible to change the pulse end tick because the end tick
304/// configuration is shared with all four channels.
305pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> {
306 inner: ManuallyDrop<Timer<'d, T>>,
307 channel: Channel,
308}
309
310impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> {
311 /// Get the end of the pulse in ticks from the trigger.
312 pub fn pulse_end(&self) -> u32 {
313 let max = self.inner.get_max_compare_value();
314 assert!(max < u32::MAX);
315 max + 1
316 }
317
318 /// Get the width of the pulse in ticks.
319 pub fn pulse_width(&mut self) -> u32 {
320 self.pulse_end().saturating_sub(self.pulse_delay())
321 }
322
323 /// Get the start of the pulse in ticks from the trigger.
324 pub fn pulse_delay(&mut self) -> u32 {
325 self.inner.get_compare_value(self.channel)
326 }
327
328 /// Set the start of the pulse in ticks from the trigger.
329 pub fn set_pulse_delay(&mut self, delay: u32) {
330 assert!(delay <= self.pulse_end());
331 self.inner.set_compare_value(self.channel, delay);
332 }
333
334 /// Set the pulse width in ticks.
335 pub fn set_pulse_width(&mut self, width: u32) {
336 assert!(width <= self.pulse_end());
337 self.set_pulse_delay(self.pulse_end() - width);
338 }
339
340 /// Waits until the trigger and following delay has passed.
341 pub async fn wait_for_pulse_start(&mut self) {
342 self.inner.enable_input_interrupt(self.channel, true);
343
344 OnePulseFuture::<T> {
345 channel: self.channel,
346 phantom: PhantomData,
347 }
348 .await
349 }
350}
351
352#[must_use = "futures do nothing unless you `.await` or poll them"]
353struct OnePulseFuture<T: GeneralInstance4Channel> {
354 channel: Channel,
355 phantom: PhantomData<T>,
356}
357
358impl<'d, T: GeneralInstance4Channel> Drop for OnePulseFuture<T> {
359 fn drop(&mut self) {
360 critical_section::with(|_| {
361 let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };
362
363 // disable interrupt enable
364 regs.dier().modify(|w| w.set_ccie(self.channel.index(), false));
365 });
366 }
367}
368
369impl<'d, T: GeneralInstance4Channel> Future for OnePulseFuture<T> {
370 type Output = ();
371
372 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
373 T::state().cc_waker[self.channel.index()].register(cx.waker());
374
375 let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };
376
377 let dier = regs.dier().read();
378 if !dier.ccie(self.channel.index()) {
379 Poll::Ready(())
380 } else {
381 Poll::Pending
382 }
383 }
384}