From 4cecbd6aa9d73ca2d493901f96f0e7c542f6de3c Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 15 Nov 2025 22:37:33 +0100 Subject: [PATCH 01/19] Move some methods on low_level timer to be available on simpler timers --- embassy-stm32/src/timer/low_level.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index f0105ece86..de61e326ba 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -505,6 +505,18 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { } } } + + pub fn set_prescaler(&mut self, psc: u16) { + self.regs_1ch().psc().write_value(psc); + } + + /// Set output compare mode. + pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { + let raw_channel: usize = channel.index(); + self.regs_1ch() + .ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } } impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { @@ -611,14 +623,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { }); } - /// Set output compare mode. - pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { - let raw_channel: usize = channel.index(); - self.regs_gp16() - .ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - /// Set output polarity. pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { self.regs_gp16() From b939fe9de375e8313c82fdf8d9f86bddcdd77226 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 15 Nov 2025 22:39:14 +0100 Subject: [PATCH 02/19] Add custom timer --- embassy-stm32/src/timer/custom_timer.rs | 268 ++++++++++++++++++++++++ embassy-stm32/src/timer/mod.rs | 1 + 2 files changed, 269 insertions(+) create mode 100644 embassy-stm32/src/timer/custom_timer.rs diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs new file mode 100644 index 0000000000..d0d83522a6 --- /dev/null +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -0,0 +1,268 @@ +use core::u16; + +use embassy_hal_internal::Peri; +use stm32_metapac::timer::vals::FilterValue; + +use crate::{ + dac::Ch2, + timer::{ + Ch1, Ch3, Ch4, CoreInstance, GeneralInstance1Channel, GeneralInstance2Channel, GeneralInstance4Channel, + TimerPin, + low_level::{CountingMode, OutputCompareMode, Timer}, + simple_pwm::PwmPin, + }, +}; + +mod ch_mode { + use crate::timer::{Channel, low_level::InputTISelection}; + + use super::*; + + pub trait Mode { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>); + } + + pub struct Unused; + pub struct Input(pub(crate) FilterValue); + pub struct Output(pub(crate) OutputCompareMode); + + impl Mode for Unused { + fn init(self, _channel: Channel, _tim: &mut Timer<'_, T>) {} + } + + impl Mode for Input { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { + tim.set_input_ti_selection(channel, InputTISelection::Normal); + tim.set_input_capture_filter(channel, self.0); + } + } + + impl Mode for Output { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { + tim.set_output_compare_mode(channel, self.0); + } + } +} + +/// Used to construct a [CustomPwm] +pub struct CustomPwmBuilder< + 'd, + T: CoreInstance, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, +> { + tim: Peri<'d, T>, + ch1: CH1, + ch2: CH2, + ch3: CH3, + ch4: CH4, + + counting_mode: CountingMode, + arr: u32, + psc: u16, +} + +impl<'d, T: CoreInstance> CustomPwmBuilder<'d, T, ch_mode::Unused, ch_mode::Unused, ch_mode::Unused, ch_mode::Unused> { + /// Construct a [CustomPwmBuilder] which can be used to construct a [CustomPwm] + pub fn new(tim: Peri<'d, T>) -> Self { + Self { + tim, + ch1: ch_mode::Unused, + ch2: ch_mode::Unused, + ch3: ch_mode::Unused, + ch4: ch_mode::Unused, + + counting_mode: CountingMode::EdgeAlignedUp, + arr: u32::MAX, + psc: 0, + } + } + + // TODO allow u32 too? + pub fn period(mut self, arr: u16) -> Self { + self.arr = arr as u32; + self + } + + pub fn prescaler(mut self, psc: u16) -> Self { + self.psc = psc; + self + } +} + +impl<'d, T: GeneralInstance1Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> + CustomPwmBuilder<'d, T, ch_mode::Unused, CH2, CH3, CH4> +{ + pub fn ch1<#[cfg(afio)] A>( + self, + _pin: if_afio!(PwmPin<'d, T, Ch1, A>), + mode: OutputCompareMode, + ) -> CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4> { + let CustomPwmBuilder { + tim, + ch1: _, + ch2, + ch3, + ch4, + counting_mode, + arr, + psc, + } = self; + CustomPwmBuilder { + tim, + ch1: ch_mode::Output(mode), + ch2, + ch3, + ch4, + counting_mode, + arr, + psc, + } + } +} + +impl<'d, T: GeneralInstance2Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> + CustomPwmBuilder<'d, T, CH1, ch_mode::Unused, CH3, CH4> +{ + pub fn ch2<#[cfg(afio)] A>( + self, + _pin: if_afio!(PwmPin<'d, T, Ch2, A>), + mode: OutputCompareMode, + ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4> { + let CustomPwmBuilder { + tim, + ch1, + ch2: _, + ch3, + ch4, + counting_mode, + arr, + psc, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2: ch_mode::Output(mode), + ch3, + ch4, + counting_mode, + arr, + psc, + } + } +} + +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4: ch_mode::Mode> + CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Unused, CH4> +{ + pub fn ch3<#[cfg(afio)] A>( + self, + _pin: if_afio!(PwmPin<'d, T, Ch3, A>), + mode: OutputCompareMode, + ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4> { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3: _, + ch4, + counting_mode, + arr, + psc, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3: ch_mode::Output(mode), + ch4, + counting_mode, + arr, + psc, + } + } +} + +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused> +{ + pub fn ch4<#[cfg(afio)] A>( + self, + _pin: if_afio!(PwmPin<'d, T, Ch4, A>), + mode: OutputCompareMode, + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output> { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4: _, + counting_mode, + arr, + psc, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4: ch_mode::Output(mode), + counting_mode, + arr, + psc, + } + } + + pub fn ch4_input( + self, + _pin: if_afio!(impl TimerPin), + filter: FilterValue, + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input> { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4: _, + counting_mode, + arr, + psc, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4: ch_mode::Input(filter), + counting_mode, + arr, + psc, + } + } +} + +impl<'d, T: GeneralInstance1Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused> +{ + pub fn finalize(self) -> CustomPwm<'d, T> { + use ch_mode::Mode; + let mut inner = Timer::new(self.tim); + + self.ch1.init(super::Channel::Ch1, &mut inner); + self.ch2.init(super::Channel::Ch2, &mut inner); + self.ch3.init(super::Channel::Ch3, &mut inner); + self.ch4.init(super::Channel::Ch4, &mut inner); + + //inner.set_counting_mode(self.counting_mode); + inner.set_max_compare_value(self.arr); + inner.set_prescaler(self.psc); + + CustomPwm { inner } + } +} + +pub struct CustomPwm<'d, T: CoreInstance> { + inner: Timer<'d, T>, +} diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 804d1ef376..f36dd5a843 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -7,6 +7,7 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(stm32l0))] pub mod complementary_pwm; +pub mod custom_timer; pub mod input_capture; pub mod low_level; pub mod one_pulse; From 4d6cff8c90d4857d7f1480eefbbd34576828b319 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 03:12:59 +0100 Subject: [PATCH 03/19] Add channels, external trigger etc. --- embassy-stm32/src/timer/custom_timer.rs | 767 ++++++++++++++++++++--- embassy-stm32/src/timer/input_capture.rs | 6 +- embassy-stm32/src/timer/low_level.rs | 17 +- 3 files changed, 706 insertions(+), 84 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index d0d83522a6..507d1a7592 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -1,20 +1,30 @@ -use core::u16; +//! Custom timer driver. +//! +//! This is flexible timer driver for all STM32 timers. Tries to give quite unhindered access to +//! most timer features while offering some type level protection for incorrect configuration. +//! +//! The available functionality depends on the timer type. +//! +use core::{marker::PhantomData, u16}; use embassy_hal_internal::Peri; -use stm32_metapac::timer::vals::FilterValue; +use stm32_metapac::timer::vals::{FilterValue, Ts}; use crate::{ - dac::Ch2, + time::Hertz, timer::{ - Ch1, Ch3, Ch4, CoreInstance, GeneralInstance1Channel, GeneralInstance2Channel, GeneralInstance4Channel, - TimerPin, - low_level::{CountingMode, OutputCompareMode, Timer}, + Ch1, Ch2, Ch3, Ch4, CoreInstance, GeneralInstance4Channel, TimerPin, + input_capture::InputCaptureFuture, + low_level::{CountingMode, InputCaptureMode, InputTISelection, OutputCompareMode, SlaveMode, Timer}, simple_pwm::PwmPin, }, }; mod ch_mode { - use crate::timer::{Channel, low_level::InputTISelection}; + use crate::timer::{ + Channel, + low_level::{InputCaptureMode, InputTISelection}, + }; use super::*; @@ -23,8 +33,16 @@ mod ch_mode { } pub struct Unused; - pub struct Input(pub(crate) FilterValue); - pub struct Output(pub(crate) OutputCompareMode); + pub struct Input { + pub(crate) filter: FilterValue, + pub(crate) mode: InputCaptureMode, + pub(crate) ti_selection: InputTISelection, + pub(crate) prescaler_factor: u8, + } + pub struct Output { + pub(crate) mode: OutputCompareMode, + pub(crate) duty: u16, + } impl Mode for Unused { fn init(self, _channel: Channel, _tim: &mut Timer<'_, T>) {} @@ -32,18 +50,38 @@ mod ch_mode { impl Mode for Input { fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { - tim.set_input_ti_selection(channel, InputTISelection::Normal); - tim.set_input_capture_filter(channel, self.0); + tim.set_input_capture_filter(channel, self.filter); + tim.set_input_capture_mode(channel, self.mode); + tim.set_input_capture_prescaler(channel, self.prescaler_factor); + tim.set_input_ti_selection(channel, self.ti_selection); } } - impl Mode for Output { + impl Mode for Output { fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { - tim.set_output_compare_mode(channel, self.0); + tim.set_output_compare_mode(channel, self.mode); + tim.set_compare_value(channel, self.duty as u32) } } } +mod trigger_source { + pub struct Internal; + pub struct Ch1; + pub struct Ch2; + pub struct Etr; +} + +mod external_trigger { + pub struct Unused; + pub struct Etr; +} + +enum Speed { + Hertz(Hertz), + Manual { arr: u32, psc: u16 }, +} + /// Used to construct a [CustomPwm] pub struct CustomPwmBuilder< 'd, @@ -52,19 +90,44 @@ pub struct CustomPwmBuilder< CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, > { tim: Peri<'d, T>, ch1: CH1, ch2: CH2, ch3: CH3, ch4: CH4, + etr: ETR, + + trigger_source: TS, + trig_source: Ts, + slave_mode: SlaveMode, + one_pulse_mode: bool, counting_mode: CountingMode, - arr: u32, - psc: u16, + speed: Speed, + + #[cfg(afio)] + _a: PhantomData, } -impl<'d, T: CoreInstance> CustomPwmBuilder<'d, T, ch_mode::Unused, ch_mode::Unused, ch_mode::Unused, ch_mode::Unused> { +impl<'d, T: CoreInstance, #[cfg(afio)] A> + if_afio!( + CustomPwmBuilder< + 'd, + T, + ch_mode::Unused, + ch_mode::Unused, + ch_mode::Unused, + ch_mode::Unused, + external_trigger::Unused, + trigger_source::Internal, + A, + > + ) +{ /// Construct a [CustomPwmBuilder] which can be used to construct a [CustomPwm] pub fn new(tim: Peri<'d, T>) -> Self { Self { @@ -73,179 +136,650 @@ impl<'d, T: CoreInstance> CustomPwmBuilder<'d, T, ch_mode::Unused, ch_mode::Unus ch2: ch_mode::Unused, ch3: ch_mode::Unused, ch4: ch_mode::Unused, - + etr: external_trigger::Unused, + one_pulse_mode: false, counting_mode: CountingMode::EdgeAlignedUp, - arr: u32::MAX, - psc: 0, + speed: Speed::Manual { arr: u32::MAX, psc: 0 }, + trigger_source: trigger_source::Internal, + trig_source: Ts::ITR0, + slave_mode: SlaveMode::DISABLED, + _a: PhantomData, } } +} + +impl< + 'd, + T: CoreInstance, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS, A>) +{ + /// Set manually frequency by specifying prescaler and period + pub fn prescaler_and_period(mut self, prescaler: u16, period_ticks: u32) -> Self { + self.speed = Speed::Manual { + arr: period_ticks, + psc: prescaler, + }; + self + } - // TODO allow u32 too? - pub fn period(mut self, arr: u16) -> Self { - self.arr = arr as u32; + /// Set frequency + pub fn frequency(mut self, hz: Hertz) -> Self { + self.speed = Speed::Hertz(hz); self } - pub fn prescaler(mut self, psc: u16) -> Self { - self.psc = psc; + /// Set one pulse mode + pub fn one_pulse_mode(mut self) -> Self { + self.one_pulse_mode = true; self } } -impl<'d, T: GeneralInstance1Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> - CustomPwmBuilder<'d, T, ch_mode::Unused, CH2, CH3, CH4> +impl< + 'd, + T: GeneralInstance4Channel, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Unused, CH2, CH3, CH4, ETR, TS, A>) { - pub fn ch1<#[cfg(afio)] A>( + /// Setup channel 1 as output + pub fn ch1( self, _pin: if_afio!(PwmPin<'d, T, Ch1, A>), mode: OutputCompareMode, - ) -> CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4> { + duty: u16, + ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS, A>) { let CustomPwmBuilder { tim, ch1: _, ch2, ch3, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } = self; CustomPwmBuilder { tim, - ch1: ch_mode::Output(mode), + ch1: ch_mode::Output { mode, duty }, ch2, ch3, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } + } + + /// Setup channel 1 as input + pub fn ch1_input( + self, + _pin: if_afio!(impl TimerPin), + filter: FilterValue, + mode: InputCaptureMode, + ti_selection: InputTISelection, + prescaler_factor: u8, + ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS, A>) { + let CustomPwmBuilder { + tim, + ch1: _, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1: ch_mode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + }, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } } } -impl<'d, T: GeneralInstance2Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> - CustomPwmBuilder<'d, T, CH1, ch_mode::Unused, CH3, CH4> +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Unused, CH3, CH4, ETR, TS, A>) { - pub fn ch2<#[cfg(afio)] A>( + /// Setup channel 2 as output + pub fn ch2( self, _pin: if_afio!(PwmPin<'d, T, Ch2, A>), mode: OutputCompareMode, - ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4> { + duty: u16, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS, A>) { let CustomPwmBuilder { tim, ch1, ch2: _, ch3, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } = self; CustomPwmBuilder { tim, ch1, - ch2: ch_mode::Output(mode), + ch2: ch_mode::Output { mode, duty }, ch3, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } + } + + /// Setup channel 2 as input + pub fn ch2_input( + self, + _pin: if_afio!(impl TimerPin), + filter: FilterValue, + mode: InputCaptureMode, + ti_selection: InputTISelection, + prescaler_factor: u8, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS, A>) { + let CustomPwmBuilder { + tim, + ch1, + ch2: _, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2: ch_mode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + }, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } } } -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4: ch_mode::Mode> - CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Unused, CH4> +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Unused, CH4, ETR, TS, A>) { - pub fn ch3<#[cfg(afio)] A>( + /// Setup channel 3 as output + pub fn ch3( self, _pin: if_afio!(PwmPin<'d, T, Ch3, A>), mode: OutputCompareMode, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4> { + duty: u16, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS, A>) { let CustomPwmBuilder { tim, ch1, ch2, ch3: _, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } = self; CustomPwmBuilder { tim, ch1, ch2, - ch3: ch_mode::Output(mode), + ch3: ch_mode::Output { mode, duty }, ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } + } + + /// Setup channel 3 as input + pub fn ch3_input( + self, + _pin: if_afio!(impl TimerPin), + filter: FilterValue, + mode: InputCaptureMode, + ti_selection: InputTISelection, + prescaler_factor: u8, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS, A>) { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3: _, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3: ch_mode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + }, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } } } -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused> +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + ETR, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused, ETR, TS, A>) { - pub fn ch4<#[cfg(afio)] A>( + /// Setup channel 4 as output + pub fn ch4( self, _pin: if_afio!(PwmPin<'d, T, Ch4, A>), mode: OutputCompareMode, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output> { + duty: u16, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS, A>) { let CustomPwmBuilder { tim, ch1, ch2, ch3, ch4: _, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } = self; CustomPwmBuilder { tim, ch1, ch2, ch3, - ch4: ch_mode::Output(mode), + ch4: ch_mode::Output { mode, duty }, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, } } - pub fn ch4_input( + /// Setup channel 3 as input + pub fn ch4_input( self, - _pin: if_afio!(impl TimerPin), + _pin: if_afio!(impl TimerPin), filter: FilterValue, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input> { + mode: InputCaptureMode, + ti_selection: InputTISelection, + prescaler_factor: u8, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS, A>) { let CustomPwmBuilder { tim, ch1, ch2, ch3, ch4: _, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4: ch_mode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + }, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } + } +} + +/// Trigger mode +pub enum TriggerMode { + /// Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter and generates an update of the registers. + ResetMode, + /// Gated Mode - The counter clock is enabled when the trigger input (TRGI) is high. The counter stops (but is not reset) as soon as the trigger becomes low. Both start and stop of the counter are controlled. + GatedMode, + /// Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not reset). Only the start of the counter is controlled. + TriggerMode, + /// External Clock Mode 1 - Rising edges of the selected trigger (TRGI) clock the counter. + ExternalClockMode, +} + +/// Trigger source be connected to the channels filter output or direcly at the edge detector +pub enum TriggerSource { + /// Connecto directly at edge detector + EdgeDetector, + + /// Connec to to channels filtered output + Filtered, +} + +impl< + 'd, + T: GeneralInstance4Channel, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal, A>) +{ + /// Setup timer to be triggered from ch1 compare match event + pub fn trigger_from_ch1( + self, + mode: TriggerMode, + source: TriggerSource, + ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1, A>) { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source: _, + trig_source: _, + slave_mode: _, + #[cfg(afio)] + _a, } = self; CustomPwmBuilder { tim, ch1, ch2, ch3, - ch4: ch_mode::Input(filter), + ch4, + etr, + one_pulse_mode, counting_mode, - arr, - psc, + speed, + trigger_source: trigger_source::Ch1, + trig_source: match source { + TriggerSource::EdgeDetector => Ts::TI1F_ED, + TriggerSource::Filtered => Ts::TI1FP1, + }, + slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }, + #[cfg(afio)] + _a, } } } -impl<'d, T: GeneralInstance1Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused> +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + ETR, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal, A>) { + /// Setup timer to be triggered from ch1 compare match event + pub fn trigger_from_ch2( + self, + mode: TriggerMode, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2, A>) { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source: _, + trig_source: _, + slave_mode: _, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source: trigger_source::Ch2, + trig_source: Ts::TI2FP2, + slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }, + #[cfg(afio)] + _a, + } + } +} + +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal, A>) +{ + /// Setup timer to be triggered from ch1 compare match event + pub fn trigger_etr( + self, + mode: TriggerMode, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr, A>) { + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source: _, + trig_source: _, + slave_mode: _, + #[cfg(afio)] + _a, + } = self; + CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source: trigger_source::Etr, + trig_source: Ts::ETRF, + slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }, + #[cfg(afio)] + _a, + } + } +} + +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, ETR, TS, A> + if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused, ETR, TS, A>) +{ + /// Finalize configuration and create the [CustomPwm] pub fn finalize(self) -> CustomPwm<'d, T> { use ch_mode::Mode; let mut inner = Timer::new(self.tim); @@ -255,14 +789,101 @@ impl<'d, T: GeneralInstance1Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode inner.set_frequency(hz), + Speed::Manual { arr, psc } => { + inner.set_max_compare_value(arr); + inner.set_prescaler(psc); + } + } + inner.generate_update_event(); + + inner.regs_core().cr1().modify(|r| r.set_opm(self.one_pulse_mode)); + inner.enable_outputs(); + inner.start(); CustomPwm { inner } } } +/// Use [CustomPwmBuilder::new] to create a new timer pub struct CustomPwm<'d, T: CoreInstance> { inner: Timer<'d, T>, } + +impl<'d, T: GeneralInstance4Channel> CustomPwm<'d, T> { + /// Set compare value + pub fn set_compare_value(&mut self, duty: u16, channel: super::Channel) { + self.inner.set_compare_value(channel, duty as u32); + } + + /// Generate a sequence of PWM waveform + /// + /// Note: + /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: super::Channel, duty: &[u16]) { + self.inner.waveform_up(dma, channel, duty).await; + } + + /// Get capture value for a channel. + pub fn get_capture_value(&self, channel: super::Channel) -> u32 { + self.inner.get_capture_value(channel) + } + + fn new_future(&self, channel: super::Channel) -> InputCaptureFuture { + self.inner.enable_channel(channel, true); + self.inner.enable_input_interrupt(channel, true); + + InputCaptureFuture { + channel, + phantom: PhantomData, + } + } + + /// Asynchronously wait until the pin sees an edge as configured on timer init. + // TODO: only allow this to be called for channels in input mode + pub async fn wait_for_configured_edge(&mut self, channel: super::Channel) -> u32 { + self.new_future(channel).await + } +} + +async fn _example( + tim: Peri<'_, crate::peripherals::TIM1>, + dma: Peri<'_, impl super::UpDma>, + trigger_pin: crate::peripherals::PA8, + out_pin: Peri<'_, crate::peripherals::PA9>, + capture_pin: crate::peripherals::PA10, +) { + let out_pin = PwmPin::<_, _, crate::gpio::AfioRemap<0>>::new(out_pin, crate::gpio::OutputType::PushPull); + + let mut tim = CustomPwmBuilder::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .ch1_input( + trigger_pin, + FilterValue::FDTS_DIV32_N8, + InputCaptureMode::BothEdges, + InputTISelection::Normal, + 1, + ) + .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) + .ch2(out_pin, OutputCompareMode::PwmMode2, 800) + .ch3_input( + capture_pin, + FilterValue::FCK_INT_N2, + InputCaptureMode::Rising, + InputTISelection::Normal, + 0, + ) + .one_pulse_mode() + .finalize(); + + tim.set_compare_value(150, super::Channel::Ch2); + tim.waveform_up(dma, super::Channel::Ch1, &[100, 400, 800, 1100, 1200]) + .await; + let _capture = tim.wait_for_configured_edge(super::Channel::Ch3).await; +} diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 9cf0f8c341..eec05d2198 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -159,9 +159,9 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } #[must_use = "futures do nothing unless you `.await` or poll them"] -struct InputCaptureFuture { - channel: Channel, - phantom: PhantomData, +pub(crate) struct InputCaptureFuture { + pub(crate) channel: Channel, + pub(crate) phantom: PhantomData, } impl Drop for InputCaptureFuture { diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index de61e326ba..6892f0f69c 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -506,17 +506,10 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { } } + /// Set prescaler pub fn set_prescaler(&mut self, psc: u16) { self.regs_1ch().psc().write_value(psc); } - - /// Set output compare mode. - pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { - let raw_channel: usize = channel.index(); - self.regs_1ch() - .ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } } impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { @@ -630,6 +623,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { .modify(|w| w.set_ccp(channel.index(), polarity.into())); } + /// Set output compare mode. + pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { + let raw_channel: usize = channel.index(); + self.regs_1ch() + .ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); From 195b8e9391344204e5ca750673429e8164c6588c Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 12:02:11 +0100 Subject: [PATCH 04/19] Add set_field macro --- embassy-stm32/src/timer/custom_timer.rs | 461 ++++-------------------- 1 file changed, 78 insertions(+), 383 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 507d1a7592..9c1914caa7 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -82,6 +82,59 @@ enum Speed { Manual { arr: u32, psc: u16 }, } +macro_rules! set_field { + ( + $this:expr + $(, tim: $tim:expr)* + $(, ch1: $ch1:expr)* + $(, ch2: $ch2:expr)* + $(, ch3: $ch3:expr)* + $(, ch4: $ch4:expr)* + $(, etr: $etr:expr)* + $(, one_pulse_mode: $one_pulse_mode:expr)* + $(, counting_mode: $counting_mode:expr)* + $(, speed: $speed:expr)* + $(, trigger_source: $trigger_source:expr)* + $(, trig_source: $trig_source:expr)* + $(, slave_mode: $slave_mode:expr)* + + ) => {{ + #[allow(unused_variables)] + let CustomPwmBuilder { + tim, + ch1, + ch2, + ch3, + ch4, + etr, + one_pulse_mode, + counting_mode, + speed, + trigger_source, + trig_source, + slave_mode, + #[cfg(afio)] + _a, + } = $this; + CustomPwmBuilder { + tim $(: $tim)*, + ch1 $(: $ch1)*, + ch2 $(: $ch2)*, + ch3 $(: $ch3)*, + ch4 $(: $ch4)*, + etr $(: $etr)*, + one_pulse_mode $(: $one_pulse_mode)*, + counting_mode $(: $counting_mode)*, + speed $(: $speed)*, + trigger_source $(: $trigger_source)*, + trig_source $(: $trig_source)*, + slave_mode $(: $slave_mode)*, + #[cfg(afio)] + _a, + }} + }; +} + /// Used to construct a [CustomPwm] pub struct CustomPwmBuilder< 'd, @@ -92,7 +145,7 @@ pub struct CustomPwmBuilder< CH4: ch_mode::Mode, ETR, TS, - #[cfg(afio)] A, + #[cfg(afio)] A = crate::gpio::AfioRemap<0>, > { tim: Peri<'d, T>, ch1: CH1, @@ -200,38 +253,7 @@ impl< mode: OutputCompareMode, duty: u16, ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1: _, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1: ch_mode::Output { mode, duty }, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch1: ch_mode::Output { mode, duty }) } /// Setup channel 1 as input @@ -243,43 +265,7 @@ impl< ti_selection: InputTISelection, prescaler_factor: u8, ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1: _, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1: ch_mode::Input { - filter, - mode, - ti_selection, - prescaler_factor, - }, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch1: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -301,38 +287,7 @@ impl< mode: OutputCompareMode, duty: u16, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2: _, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2: ch_mode::Output { mode, duty }, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch2: ch_mode::Output { mode, duty }) } /// Setup channel 2 as input @@ -344,43 +299,7 @@ impl< ti_selection: InputTISelection, prescaler_factor: u8, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2: _, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2: ch_mode::Input { - filter, - mode, - ti_selection, - prescaler_factor, - }, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch2: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -402,38 +321,7 @@ impl< mode: OutputCompareMode, duty: u16, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3: _, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3: ch_mode::Output { mode, duty }, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch3: ch_mode::Output { mode, duty }) } /// Setup channel 3 as input @@ -445,43 +333,7 @@ impl< ti_selection: InputTISelection, prescaler_factor: u8, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3: _, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3: ch_mode::Input { - filter, - mode, - ti_selection, - prescaler_factor, - }, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch3: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -503,38 +355,7 @@ impl< mode: OutputCompareMode, duty: u16, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4: _, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4: ch_mode::Output { mode, duty }, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch4: ch_mode::Output { mode, duty }) } /// Setup channel 3 as input @@ -546,43 +367,7 @@ impl< ti_selection: InputTISelection, prescaler_factor: u8, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4: _, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4: ch_mode::Input { - filter, - mode, - ti_selection, - prescaler_factor, - }, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - #[cfg(afio)] - _a, - } + set_field!(self, ch4: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -623,33 +408,7 @@ impl< mode: TriggerMode, source: TriggerSource, ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: _, - trig_source: _, - slave_mode: _, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: trigger_source::Ch1, + set_field!(self, trigger_source: trigger_source::Ch1, trig_source: match source { TriggerSource::EdgeDetector => Ts::TI1F_ED, TriggerSource::Filtered => Ts::TI1FP1, @@ -659,10 +418,8 @@ impl< TriggerMode::GatedMode => SlaveMode::GATED_MODE, TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - }, - #[cfg(afio)] - _a, - } + } + ) } } @@ -681,43 +438,12 @@ impl< self, mode: TriggerMode, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: _, - trig_source: _, - slave_mode: _, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: trigger_source::Ch2, - trig_source: Ts::TI2FP2, - slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - }, - #[cfg(afio)] - _a, - } + set_field!(self, trigger_source: trigger_source::Ch2, trig_source: Ts::TI2FP2, slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }) } } @@ -736,43 +462,12 @@ impl< self, mode: TriggerMode, ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr, A>) { - let CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: _, - trig_source: _, - slave_mode: _, - #[cfg(afio)] - _a, - } = self; - CustomPwmBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source: trigger_source::Etr, - trig_source: Ts::ETRF, - slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - }, - #[cfg(afio)] - _a, - } + set_field!(self, trigger_source: trigger_source::Etr, trig_source: Ts::ETRF, slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }) } } @@ -858,7 +553,7 @@ async fn _example( out_pin: Peri<'_, crate::peripherals::PA9>, capture_pin: crate::peripherals::PA10, ) { - let out_pin = PwmPin::<_, _, crate::gpio::AfioRemap<0>>::new(out_pin, crate::gpio::OutputType::PushPull); + let out_pin: PwmPin<'_, _, _, crate::gpio::AfioRemap<0>> = PwmPin::new(out_pin, crate::gpio::OutputType::PushPull); let mut tim = CustomPwmBuilder::new(tim) //.frequency(Hertz(123)) From 048d7e7f3dce28e36bfc6667e6eb3173cb0a13bd Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 12:45:27 +0100 Subject: [PATCH 05/19] Undo move of low_level method --- embassy-stm32/src/timer/low_level.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 6892f0f69c..5246282c2f 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -616,21 +616,21 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { }); } - /// Set output polarity. - pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { - self.regs_gp16() - .ccer() - .modify(|w| w.set_ccp(channel.index(), polarity.into())); - } - /// Set output compare mode. pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { let raw_channel: usize = channel.index(); - self.regs_1ch() + self.regs_gp16() .ccmr_output(raw_channel / 2) .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + /// Set output polarity. + pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { + self.regs_gp16() + .ccer() + .modify(|w| w.set_ccp(channel.index(), polarity.into())); + } + /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); From f6d71d8e862d7e6eb026743c8b13b440af220a07 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 13:50:18 +0100 Subject: [PATCH 06/19] Trigger by etr --- embassy-stm32/src/timer/custom_timer.rs | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 9c1914caa7..4fd657339d 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -392,6 +392,33 @@ pub enum TriggerSource { Filtered, } +impl< + 'd, + T: GeneralInstance4Channel, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal, A>) +{ + /// Setup timer to be triggered from ch1 compare match event + pub fn trigger_from_etr( + self, + mode: TriggerMode, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr, A>) { + set_field!(self, trigger_source: trigger_source::Etr, + trig_source: Ts::TI1F_ED, + slave_mode: match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + } + ) + } +} + impl< 'd, T: GeneralInstance4Channel, From cf60e65bccbc80ddfd86a1b369ca59578dcc22f8 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 15:09:59 +0100 Subject: [PATCH 07/19] More work on etr --- embassy-stm32/src/timer/custom_timer.rs | 246 +++++++++++++++--------- 1 file changed, 155 insertions(+), 91 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 4fd657339d..eba75aa10f 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -8,12 +8,12 @@ use core::{marker::PhantomData, u16}; use embassy_hal_internal::Peri; -use stm32_metapac::timer::vals::{FilterValue, Ts}; +use stm32_metapac::timer::vals::{Etp, Etps, FilterValue, Ts}; use crate::{ time::Hertz, timer::{ - Ch1, Ch2, Ch3, Ch4, CoreInstance, GeneralInstance4Channel, TimerPin, + Ch1, Ch2, Ch3, Ch4, CoreInstance, ExternalTriggerPin, GeneralInstance4Channel, TimerPin, input_capture::InputCaptureFuture, low_level::{CountingMode, InputCaptureMode, InputTISelection, OutputCompareMode, SlaveMode, Timer}, simple_pwm::PwmPin, @@ -28,11 +28,13 @@ mod ch_mode { use super::*; - pub trait Mode { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>); + pub trait Mode { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>); } - pub struct Unused; + pub struct InternalOutput { + pub(crate) duty: u16, + } pub struct Input { pub(crate) filter: FilterValue, pub(crate) mode: InputCaptureMode, @@ -44,12 +46,15 @@ mod ch_mode { pub(crate) duty: u16, } - impl Mode for Unused { - fn init(self, _channel: Channel, _tim: &mut Timer<'_, T>) {} + impl Mode for InternalOutput { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { + tim.set_output_compare_mode(channel, OutputCompareMode::Frozen); + tim.set_compare_value(channel, self.duty as u32) + } } - impl Mode for Input { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { + impl Mode for Input { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { tim.set_input_capture_filter(channel, self.filter); tim.set_input_capture_mode(channel, self.mode); tim.set_input_capture_prescaler(channel, self.prescaler_factor); @@ -57,8 +62,8 @@ mod ch_mode { } } - impl Mode for Output { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { + impl Mode for Output { + fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { tim.set_output_compare_mode(channel, self.mode); tim.set_compare_value(channel, self.duty as u32) } @@ -73,8 +78,35 @@ mod trigger_source { } mod external_trigger { + use stm32_metapac::timer::vals::{Etp, Etps}; + + use super::*; + + pub trait Trigger { + fn init(self, tim: &mut Timer<'_, T>); + } pub struct Unused; - pub struct Etr; + pub struct Etr { + pub(crate) filter: FilterValue, + pub(crate) polarity: Etp, + pub(crate) trigger_prescaler: Etps, + } + + impl Trigger for Unused { + fn init(self, _tim: &mut Timer<'_, T>) {} + } + + impl Trigger for Etr { + fn init(self, tim: &mut Timer<'_, T>) { + tim.regs_gp16().af1().modify(|w| w.set_etrsel(0)); // 0: ETR input + tim.regs_gp16().smcr().modify(|w| { + w.set_etf(self.filter); + w.set_ece(false); // <--- TODO: I really need to look into how to set this and the SMS bits + w.set_etp(self.polarity); + w.set_etps(self.trigger_prescaler); + }); + } + } } enum Speed { @@ -139,10 +171,10 @@ macro_rules! set_field { pub struct CustomPwmBuilder< 'd, T: CoreInstance, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, ETR, TS, #[cfg(afio)] A = crate::gpio::AfioRemap<0>, @@ -171,10 +203,10 @@ impl<'d, T: CoreInstance, #[cfg(afio)] A> CustomPwmBuilder< 'd, T, - ch_mode::Unused, - ch_mode::Unused, - ch_mode::Unused, - ch_mode::Unused, + ch_mode::InternalOutput, + ch_mode::InternalOutput, + ch_mode::InternalOutput, + ch_mode::InternalOutput, external_trigger::Unused, trigger_source::Internal, A, @@ -185,10 +217,10 @@ impl<'d, T: CoreInstance, #[cfg(afio)] A> pub fn new(tim: Peri<'d, T>) -> Self { Self { tim, - ch1: ch_mode::Unused, - ch2: ch_mode::Unused, - ch3: ch_mode::Unused, - ch4: ch_mode::Unused, + ch1: ch_mode::InternalOutput { duty: 0 }, + ch2: ch_mode::InternalOutput { duty: 0 }, + ch3: ch_mode::InternalOutput { duty: 0 }, + ch4: ch_mode::InternalOutput { duty: 0 }, etr: external_trigger::Unused, one_pulse_mode: false, counting_mode: CountingMode::EdgeAlignedUp, @@ -204,10 +236,10 @@ impl<'d, T: CoreInstance, #[cfg(afio)] A> impl< 'd, T: CoreInstance, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, ETR, TS, #[cfg(afio)] A, @@ -238,14 +270,42 @@ impl< impl< 'd, T: GeneralInstance4Channel, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, + TS, + #[cfg(afio)] A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS, A>) +{ + /// Setup channel 1 as output + pub fn etr( + self, + _pin: if_afio!(impl ExternalTriggerPin), + filter: FilterValue, + polarity: Etp, + trigger_prescaler: Etps, + ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS, A>) { + set_field!(self, etr: external_trigger::Etr{filter, polarity, trigger_prescaler }) + } +} + +impl< + 'd, + T: GeneralInstance4Channel, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, ETR, TS, #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Unused, CH2, CH3, CH4, ETR, TS, A>) +> if_afio!(CustomPwmBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS, A>) { + /// Set ch1 to be used as internal output, can be used as time base etc + pub fn ch1_internal(self, duty: u16) -> Self { + set_field!(self, ch1: ch_mode::InternalOutput { duty }) + } + /// Setup channel 1 as output pub fn ch1( self, @@ -272,14 +332,19 @@ impl< impl< 'd, T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, ETR, TS, #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Unused, CH3, CH4, ETR, TS, A>) +> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS, A>) { + /// Set ch2 to be used as internal output, can be used as time base etc + pub fn ch2_internal(self, duty: u16) -> Self { + set_field!(self, ch2: ch_mode::InternalOutput { duty }) + } + /// Setup channel 2 as output pub fn ch2( self, @@ -306,14 +371,19 @@ impl< impl< 'd, T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH4: ch_mode::Mode, ETR, TS, #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Unused, CH4, ETR, TS, A>) +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS, A>) { + /// Set ch3 to be used as internal output, can be used as time base etc + pub fn ch3_internal(self, duty: u16) -> Self { + set_field!(self, ch3: ch_mode::InternalOutput { duty }) + } + /// Setup channel 3 as output pub fn ch3( self, @@ -340,14 +410,19 @@ impl< impl< 'd, T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, ETR, TS, #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused, ETR, TS, A>) +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS, A>) { + /// Set ch4 to be used as internal output, can be used as time base etc + pub fn ch4_internal(self, duty: u16) -> Self { + set_field!(self, ch4: ch_mode::InternalOutput { duty }) + } + /// Setup channel 4 as output pub fn ch4( self, @@ -395,10 +470,10 @@ pub enum TriggerSource { impl< 'd, T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + CH4: ch_mode::Mode, #[cfg(afio)] A, > if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal, A>) { @@ -419,15 +494,8 @@ impl< } } -impl< - 'd, - T: GeneralInstance4Channel, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal, A>) +impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, #[cfg(afio)] A> + if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal, A>) { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch1( @@ -450,15 +518,8 @@ impl< } } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, #[cfg(afio)] A> + if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal, A>) { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch2( @@ -477,29 +538,13 @@ impl< impl< 'd, T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal, A>) -{ - /// Setup timer to be triggered from ch1 compare match event - pub fn trigger_etr( - self, - mode: TriggerMode, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr, A>) { - set_field!(self, trigger_source: trigger_source::Etr, trig_source: Ts::ETRF, slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - }) - } -} - -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, ETR, TS, A> - if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Unused, ETR, TS, A>) + CH1: ch_mode::Mode, + CH2: ch_mode::Mode, + CH3: ch_mode::Mode, + ETR: external_trigger::Trigger, + TS, + A, +> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS, A>) { /// Finalize configuration and create the [CustomPwm] pub fn finalize(self) -> CustomPwm<'d, T> { @@ -510,6 +555,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH2: ch_mode::Mode { - inner: Timer<'d, T>, + pub(crate) inner: Timer<'d, T>, } impl<'d, T: GeneralInstance4Channel> CustomPwm<'d, T> { @@ -567,10 +613,14 @@ impl<'d, T: GeneralInstance4Channel> CustomPwm<'d, T> { } /// Asynchronously wait until the pin sees an edge as configured on timer init. - // TODO: only allow this to be called for channels in input mode pub async fn wait_for_configured_edge(&mut self, channel: super::Channel) -> u32 { self.new_future(channel).await } + + /// Asynchronously wait until the period event + pub async fn wait_for_period(&mut self) -> u32 { + todo!() + } } async fn _example( @@ -609,3 +659,17 @@ async fn _example( .await; let _capture = tim.wait_for_configured_edge(super::Channel::Ch3).await; } + +async fn _example2(tim: Peri<'_, crate::peripherals::TIM1>, trigger_pin: crate::peripherals::PA12) { + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _, crate::gpio::AfioRemap<0>>::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .etr(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) + .trigger_from_etr(TriggerMode::TriggerMode) + .ch1_internal(1234) + .one_pulse_mode() + .finalize(); + + // Should trigger 1234 ticks after PA12 goes high + let _capture = tim.wait_for_configured_edge(super::Channel::Ch1).await; +} From 058ec21db7529908b81155d9ddd8fbdd9c83a78d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 15:30:55 +0100 Subject: [PATCH 08/19] Dont store A --- embassy-stm32/src/timer/custom_timer.rs | 146 +++++++----------------- 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index eba75aa10f..28dbaf907d 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -145,8 +145,6 @@ macro_rules! set_field { trigger_source, trig_source, slave_mode, - #[cfg(afio)] - _a, } = $this; CustomPwmBuilder { tim $(: $tim)*, @@ -161,8 +159,6 @@ macro_rules! set_field { trigger_source $(: $trigger_source)*, trig_source $(: $trig_source)*, slave_mode $(: $slave_mode)*, - #[cfg(afio)] - _a, }} }; } @@ -177,7 +173,6 @@ pub struct CustomPwmBuilder< CH4: ch_mode::Mode, ETR, TS, - #[cfg(afio)] A = crate::gpio::AfioRemap<0>, > { tim: Peri<'d, T>, ch1: CH1, @@ -193,12 +188,9 @@ pub struct CustomPwmBuilder< one_pulse_mode: bool, counting_mode: CountingMode, speed: Speed, - - #[cfg(afio)] - _a: PhantomData, } -impl<'d, T: CoreInstance, #[cfg(afio)] A> +impl<'d, T: CoreInstance> if_afio!( CustomPwmBuilder< 'd, @@ -209,7 +201,6 @@ impl<'d, T: CoreInstance, #[cfg(afio)] A> ch_mode::InternalOutput, external_trigger::Unused, trigger_source::Internal, - A, > ) { @@ -228,22 +219,12 @@ impl<'d, T: CoreInstance, #[cfg(afio)] A> trigger_source: trigger_source::Internal, trig_source: Ts::ITR0, slave_mode: SlaveMode::DISABLED, - _a: PhantomData, } } } -impl< - 'd, - T: CoreInstance, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS, A>) +impl<'d, T: CoreInstance, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS> { /// Set manually frequency by specifying prescaler and period pub fn prescaler_and_period(mut self, prescaler: u16, period_ticks: u32) -> Self { @@ -267,39 +248,23 @@ impl< } } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, TS> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS> { /// Setup channel 1 as output - pub fn etr( + pub fn etr<#[cfg(afio)] A>( self, _pin: if_afio!(impl ExternalTriggerPin), filter: FilterValue, polarity: Etp, trigger_prescaler: Etps, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS> { set_field!(self, etr: external_trigger::Etr{filter, polarity, trigger_prescaler }) } } -impl< - 'd, - T: GeneralInstance4Channel, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS, A>) +impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> + CustomPwmBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS> { /// Set ch1 to be used as internal output, can be used as time base etc pub fn ch1_internal(self, duty: u16) -> Self { @@ -307,38 +272,30 @@ impl< } /// Setup channel 1 as output - pub fn ch1( + pub fn ch1<#[cfg(afio)] A>( self, _pin: if_afio!(PwmPin<'d, T, Ch1, A>), mode: OutputCompareMode, duty: u16, - ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS> { set_field!(self, ch1: ch_mode::Output { mode, duty }) } /// Setup channel 1 as input - pub fn ch1_input( + pub fn ch1_input<#[cfg(afio)] A>( self, _pin: if_afio!(impl TimerPin), filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS> { set_field!(self, ch1: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> + CustomPwmBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS> { /// Set ch2 to be used as internal output, can be used as time base etc pub fn ch2_internal(self, duty: u16) -> Self { @@ -346,38 +303,30 @@ impl< } /// Setup channel 2 as output - pub fn ch2( + pub fn ch2<#[cfg(afio)] A>( self, _pin: if_afio!(PwmPin<'d, T, Ch2, A>), mode: OutputCompareMode, duty: u16, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS> { set_field!(self, ch2: ch_mode::Output { mode, duty }) } /// Setup channel 2 as input - pub fn ch2_input( + pub fn ch2_input<#[cfg(afio)] A>( self, _pin: if_afio!(impl TimerPin), filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS> { set_field!(self, ch2: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> + CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS> { /// Set ch3 to be used as internal output, can be used as time base etc pub fn ch3_internal(self, duty: u16) -> Self { @@ -385,38 +334,30 @@ impl< } /// Setup channel 3 as output - pub fn ch3( + pub fn ch3<#[cfg(afio)] A>( self, _pin: if_afio!(PwmPin<'d, T, Ch3, A>), mode: OutputCompareMode, duty: u16, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS> { set_field!(self, ch3: ch_mode::Output { mode, duty }) } /// Setup channel 3 as input - pub fn ch3_input( + pub fn ch3_input<#[cfg(afio)] A>( self, _pin: if_afio!(impl TimerPin), filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS> { set_field!(self, ch3: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - ETR, - TS, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, ETR, TS> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> { /// Set ch4 to be used as internal output, can be used as time base etc pub fn ch4_internal(self, duty: u16) -> Self { @@ -424,24 +365,24 @@ impl< } /// Setup channel 4 as output - pub fn ch4( + pub fn ch4<#[cfg(afio)] A>( self, _pin: if_afio!(PwmPin<'d, T, Ch4, A>), mode: OutputCompareMode, duty: u16, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS> { set_field!(self, ch4: ch_mode::Output { mode, duty }) } /// Setup channel 3 as input - pub fn ch4_input( + pub fn ch4_input<#[cfg(afio)] A>( self, _pin: if_afio!(impl TimerPin), filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS> { set_field!(self, ch4: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -467,21 +408,14 @@ pub enum TriggerSource { Filtered, } -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - #[cfg(afio)] A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal, A>) +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> + CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_etr( self, mode: TriggerMode, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr> { set_field!(self, trigger_source: trigger_source::Etr, trig_source: Ts::TI1F_ED, slave_mode: match mode { @@ -494,15 +428,15 @@ impl< } } -impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, #[cfg(afio)] A> - if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal, A>) +impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> + CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch1( self, mode: TriggerMode, source: TriggerSource, - ) -> if_afio!(CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1, A>) { + ) -> CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1> { set_field!(self, trigger_source: trigger_source::Ch1, trig_source: match source { TriggerSource::EdgeDetector => Ts::TI1F_ED, @@ -519,13 +453,13 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, #[cfg(afio)] A> - if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal, A>) + CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch2( self, mode: TriggerMode, - ) -> if_afio!(CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2, A>) { + ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2> { set_field!(self, trigger_source: trigger_source::Ch2, trig_source: Ts::TI2FP2, slave_mode: match mode { TriggerMode::ResetMode => SlaveMode::RESET_MODE, TriggerMode::GatedMode => SlaveMode::GATED_MODE, @@ -544,7 +478,7 @@ impl< ETR: external_trigger::Trigger, TS, A, -> if_afio!(CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS, A>) +> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> { /// Finalize configuration and create the [CustomPwm] pub fn finalize(self) -> CustomPwm<'d, T> { From 5d975c249c03dddbb2c900532434443bb621668b Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 15:44:29 +0100 Subject: [PATCH 09/19] Dont store A - fix leftovers --- embassy-stm32/src/timer/custom_timer.rs | 27 +++++++++++-------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 28dbaf907d..4265ef172b 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -191,18 +191,16 @@ pub struct CustomPwmBuilder< } impl<'d, T: CoreInstance> - if_afio!( - CustomPwmBuilder< - 'd, - T, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - external_trigger::Unused, - trigger_source::Internal, - > - ) + CustomPwmBuilder< + 'd, + T, + ch_mode::InternalOutput, + ch_mode::InternalOutput, + ch_mode::InternalOutput, + ch_mode::InternalOutput, + external_trigger::Unused, + trigger_source::Internal, + > { /// Construct a [CustomPwmBuilder] which can be used to construct a [CustomPwm] pub fn new(tim: Peri<'d, T>) -> Self { @@ -452,7 +450,7 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 } } -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, #[cfg(afio)] A> +impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event @@ -477,7 +475,6 @@ impl< CH3: ch_mode::Mode, ETR: external_trigger::Trigger, TS, - A, > CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> { /// Finalize configuration and create the [CustomPwm] @@ -595,7 +592,7 @@ async fn _example( } async fn _example2(tim: Peri<'_, crate::peripherals::TIM1>, trigger_pin: crate::peripherals::PA12) { - let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _, crate::gpio::AfioRemap<0>>::new(tim) + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) //.frequency(Hertz(123)) .prescaler_and_period(0, 1337) .etr(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) From 6b6d75a0cb0b21bf805604007d3cf7c908e24e62 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Nov 2025 10:37:04 -0600 Subject: [PATCH 10/19] fix custom timer example --- embassy-stm32/src/timer/custom_timer.rs | 51 ------------------ examples/stm32f1/src/bin/pwm_custom.rs | 72 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 examples/stm32f1/src/bin/pwm_custom.rs diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 4265ef172b..9880cd80ce 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -553,54 +553,3 @@ impl<'d, T: GeneralInstance4Channel> CustomPwm<'d, T> { todo!() } } - -async fn _example( - tim: Peri<'_, crate::peripherals::TIM1>, - dma: Peri<'_, impl super::UpDma>, - trigger_pin: crate::peripherals::PA8, - out_pin: Peri<'_, crate::peripherals::PA9>, - capture_pin: crate::peripherals::PA10, -) { - let out_pin: PwmPin<'_, _, _, crate::gpio::AfioRemap<0>> = PwmPin::new(out_pin, crate::gpio::OutputType::PushPull); - - let mut tim = CustomPwmBuilder::new(tim) - //.frequency(Hertz(123)) - .prescaler_and_period(0, 1337) - .ch1_input( - trigger_pin, - FilterValue::FDTS_DIV32_N8, - InputCaptureMode::BothEdges, - InputTISelection::Normal, - 1, - ) - .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) - .ch2(out_pin, OutputCompareMode::PwmMode2, 800) - .ch3_input( - capture_pin, - FilterValue::FCK_INT_N2, - InputCaptureMode::Rising, - InputTISelection::Normal, - 0, - ) - .one_pulse_mode() - .finalize(); - - tim.set_compare_value(150, super::Channel::Ch2); - tim.waveform_up(dma, super::Channel::Ch1, &[100, 400, 800, 1100, 1200]) - .await; - let _capture = tim.wait_for_configured_edge(super::Channel::Ch3).await; -} - -async fn _example2(tim: Peri<'_, crate::peripherals::TIM1>, trigger_pin: crate::peripherals::PA12) { - let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) - //.frequency(Hertz(123)) - .prescaler_and_period(0, 1337) - .etr(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) - .trigger_from_etr(TriggerMode::TriggerMode) - .ch1_internal(1234) - .one_pulse_mode() - .finalize(); - - // Should trigger 1234 ticks after PA12 goes high - let _capture = tim.wait_for_configured_edge(super::Channel::Ch1).await; -} diff --git a/examples/stm32f1/src/bin/pwm_custom.rs b/examples/stm32f1/src/bin/pwm_custom.rs new file mode 100644 index 0000000000..0774d48dd0 --- /dev/null +++ b/examples/stm32f1/src/bin/pwm_custom.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; +use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; +use embassy_stm32::timer::{Channel, UpDma}; + +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::{Peri, peripherals}; +use {defmt_rtt as _, panic_probe as _}; + +/// Connect PA0 and PC13 with a 1k Ohm resistor + +async fn _example( + tim: Peri<'_, crate::peripherals::TIM1>, + dma: Peri<'_, impl UpDma>, + trigger_pin: crate::peripherals::PA8, + out_pin: Peri<'_, crate::peripherals::PA9>, + capture_pin: crate::peripherals::PA10, +) { + let out_pin = PwmPin::new(out_pin, OutputType::PushPull); + + let mut tim = CustomPwmBuilder::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .ch1_input::>( + trigger_pin, + FilterValue::FDTS_DIV32_N8, + InputCaptureMode::BothEdges, + InputTISelection::Normal, + 1, + ) + .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) + .ch2::>(out_pin, OutputCompareMode::PwmMode2, 800) + .ch3_input::>( + capture_pin, + FilterValue::FCK_INT_N2, + InputCaptureMode::Rising, + InputTISelection::Normal, + 0, + ) + .one_pulse_mode() + .finalize(); + + tim.set_compare_value(150, Channel::Ch2); + tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; + let _capture = tim.wait_for_configured_edge(Channel::Ch3).await; +} + +async fn _example2(tim: Peri<'_, crate::peripherals::TIM1>, trigger_pin: crate::peripherals::PA12) { + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) + .trigger_from_etr(TriggerMode::TriggerMode) + .ch1_internal(1234) + .one_pulse_mode() + .finalize(); + + // Should trigger 1234 ticks after PA12 goes high + let _capture = tim.wait_for_configured_edge(Channel::Ch1).await; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = embassy_stm32::init(Default::default()); + info!("Hello World!"); +} From b58db3b038efc3c9a42a9a5e17e54f998d0fdebd Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:13:56 +0100 Subject: [PATCH 11/19] Cleanup F1 examples a bit --- examples/stm32f1/src/bin/pwm_custom.rs | 72 ---------------- examples/stm32f1/src/bin/pwm_custom_etr.rs | 67 +++++++++++++++ examples/stm32f1/src/bin/pwm_custom_multi.rs | 86 ++++++++++++++++++++ 3 files changed, 153 insertions(+), 72 deletions(-) delete mode 100644 examples/stm32f1/src/bin/pwm_custom.rs create mode 100644 examples/stm32f1/src/bin/pwm_custom_etr.rs create mode 100644 examples/stm32f1/src/bin/pwm_custom_multi.rs diff --git a/examples/stm32f1/src/bin/pwm_custom.rs b/examples/stm32f1/src/bin/pwm_custom.rs deleted file mode 100644 index 0774d48dd0..0000000000 --- a/examples/stm32f1/src/bin/pwm_custom.rs +++ /dev/null @@ -1,72 +0,0 @@ -#![no_std] -#![no_main] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_stm32::gpio::{AfioRemap, OutputType}; -use embassy_stm32::pac::timer::vals::{Etp, Etps}; -use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; -use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; -use embassy_stm32::timer::{Channel, UpDma}; - -use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::{Peri, peripherals}; -use {defmt_rtt as _, panic_probe as _}; - -/// Connect PA0 and PC13 with a 1k Ohm resistor - -async fn _example( - tim: Peri<'_, crate::peripherals::TIM1>, - dma: Peri<'_, impl UpDma>, - trigger_pin: crate::peripherals::PA8, - out_pin: Peri<'_, crate::peripherals::PA9>, - capture_pin: crate::peripherals::PA10, -) { - let out_pin = PwmPin::new(out_pin, OutputType::PushPull); - - let mut tim = CustomPwmBuilder::new(tim) - //.frequency(Hertz(123)) - .prescaler_and_period(0, 1337) - .ch1_input::>( - trigger_pin, - FilterValue::FDTS_DIV32_N8, - InputCaptureMode::BothEdges, - InputTISelection::Normal, - 1, - ) - .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) - .ch2::>(out_pin, OutputCompareMode::PwmMode2, 800) - .ch3_input::>( - capture_pin, - FilterValue::FCK_INT_N2, - InputCaptureMode::Rising, - InputTISelection::Normal, - 0, - ) - .one_pulse_mode() - .finalize(); - - tim.set_compare_value(150, Channel::Ch2); - tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; - let _capture = tim.wait_for_configured_edge(Channel::Ch3).await; -} - -async fn _example2(tim: Peri<'_, crate::peripherals::TIM1>, trigger_pin: crate::peripherals::PA12) { - let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) - //.frequency(Hertz(123)) - .prescaler_and_period(0, 1337) - .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) - .trigger_from_etr(TriggerMode::TriggerMode) - .ch1_internal(1234) - .one_pulse_mode() - .finalize(); - - // Should trigger 1234 ticks after PA12 goes high - let _capture = tim.wait_for_configured_edge(Channel::Ch1).await; -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = embassy_stm32::init(Default::default()); - info!("Hello World!"); -} diff --git a/examples/stm32f1/src/bin/pwm_custom_etr.rs b/examples/stm32f1/src/bin/pwm_custom_etr.rs new file mode 100644 index 0000000000..564971d9df --- /dev/null +++ b/examples/stm32f1/src/bin/pwm_custom_etr.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; +use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; +use embassy_stm32::timer::{Channel, UpDma}; + +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::{Peri, peripherals}; +use {defmt_rtt as _, panic_probe as _}; + +/// 0 CCR ARR 0 CCR +/// . * . . * +/// . | . . | +/// . | . . | +/// wait for . | . . | +/// cfgd edge . | . . | +/// . | . . | +/// . | . . | +/// ----------------------------*---------------------------------------------*------- +/// . . . +/// *---------------------------------------------* +/// | . | +/// | . | +/// trigger | . | +/// | . | +/// | . | +/// | . | +/// -----------* . *------------------------ +/// . . +/// . * +/// . . * | . +/// . . * | . * +/// counter . . * | . * +/// . . * | . * +/// . . * | . * +/// * *---------* + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + let dma = p.DMA1_CH5; + let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); + let trigger_pin = p.PA8; + let capture_pin = p.PA10; + + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) + .trigger_from_etr(TriggerMode::TriggerMode) + .ch1_internal(1234) + .one_pulse_mode() + .finalize(); + + loop { + // Should trigger 1234 ticks after PA12 goes high + tim.wait_for_configured_edge(Channel::Ch1).await; + + info!("Edge detected 1234 ticks ago!"); + } +} diff --git a/examples/stm32f1/src/bin/pwm_custom_multi.rs b/examples/stm32f1/src/bin/pwm_custom_multi.rs new file mode 100644 index 0000000000..7b32aed3b3 --- /dev/null +++ b/examples/stm32f1/src/bin/pwm_custom_multi.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; +use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; +use embassy_stm32::timer::{Channel, UpDma}; + +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::{Peri, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// 0 CCR ARR 0 CCR +/// . *----------------* . *------- +/// . | | . | +/// . | | . | +/// output . | | . | +/// . | | . | +/// . | | . | +/// . | | . | +/// ----------------------------* *----------------------------* +/// . . . +/// *---------------------------------------------* +/// | . | +/// | . | +/// trigger | . | +/// | . | +/// | . | +/// | . | +/// -----------* . *------------------------ +/// . . +/// . * +/// . . * | . +/// . . * | . * +/// counter . . * | . * +/// . . * | . * +/// . . * | . * +/// * *---------* +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + let dma = p.DMA1_CH5; + let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); + let trigger_pin = p.PA8; + let capture_pin = p.PA10; + + let mut tim = CustomPwmBuilder::new(p.TIM1) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .ch1_input::>( + trigger_pin, + FilterValue::FDTS_DIV32_N8, + InputCaptureMode::BothEdges, + InputTISelection::Normal, + 1, + ) + .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) + .ch2::>(out_pin, OutputCompareMode::PwmMode2, 800) + .ch3_input::>( + capture_pin, + FilterValue::FCK_INT_N2, + InputCaptureMode::Rising, + InputTISelection::Normal, + 0, + ) + .one_pulse_mode() + .finalize(); + + loop { + info!("Manually set pulse width"); + tim.set_compare_value(150, Channel::Ch2); + Timer::after_millis(300).await; + + info!("Send waveform on PA9"); + tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; + + info!("Waiting for rising edge on PA10"); + let capture = tim.wait_for_configured_edge(Channel::Ch3).await; + info!("Rising edge detected on PA10 {} ticks from trigger on PA8", capture); + } +} From 7600770411fa90615df62ed412793c5e44279200 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:14:45 +0100 Subject: [PATCH 12/19] Add H5 examples --- examples/stm32h5/src/bin/pwm_custom_etr.rs | 67 +++++++++++++++ examples/stm32h5/src/bin/pwm_custom_multi.rs | 86 ++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 examples/stm32h5/src/bin/pwm_custom_etr.rs create mode 100644 examples/stm32h5/src/bin/pwm_custom_multi.rs diff --git a/examples/stm32h5/src/bin/pwm_custom_etr.rs b/examples/stm32h5/src/bin/pwm_custom_etr.rs new file mode 100644 index 0000000000..564971d9df --- /dev/null +++ b/examples/stm32h5/src/bin/pwm_custom_etr.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; +use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; +use embassy_stm32::timer::{Channel, UpDma}; + +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::{Peri, peripherals}; +use {defmt_rtt as _, panic_probe as _}; + +/// 0 CCR ARR 0 CCR +/// . * . . * +/// . | . . | +/// . | . . | +/// wait for . | . . | +/// cfgd edge . | . . | +/// . | . . | +/// . | . . | +/// ----------------------------*---------------------------------------------*------- +/// . . . +/// *---------------------------------------------* +/// | . | +/// | . | +/// trigger | . | +/// | . | +/// | . | +/// | . | +/// -----------* . *------------------------ +/// . . +/// . * +/// . . * | . +/// . . * | . * +/// counter . . * | . * +/// . . * | . * +/// . . * | . * +/// * *---------* + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + let dma = p.DMA1_CH5; + let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); + let trigger_pin = p.PA8; + let capture_pin = p.PA10; + + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) + .trigger_from_etr(TriggerMode::TriggerMode) + .ch1_internal(1234) + .one_pulse_mode() + .finalize(); + + loop { + // Should trigger 1234 ticks after PA12 goes high + tim.wait_for_configured_edge(Channel::Ch1).await; + + info!("Edge detected 1234 ticks ago!"); + } +} diff --git a/examples/stm32h5/src/bin/pwm_custom_multi.rs b/examples/stm32h5/src/bin/pwm_custom_multi.rs new file mode 100644 index 0000000000..ca7a737b2b --- /dev/null +++ b/examples/stm32h5/src/bin/pwm_custom_multi.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; +use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; +use embassy_stm32::timer::{Channel, UpDma}; + +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::{Peri, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// 0 CCR ARR 0 CCR +/// . *----------------* . *------- +/// . | | . | +/// . | | . | +/// output . | | . | +/// . | | . | +/// . | | . | +/// . | | . | +/// ----------------------------* *----------------------------* +/// . . . +/// *---------------------------------------------* +/// | . | +/// | . | +/// trigger | . | +/// | . | +/// | . | +/// | . | +/// -----------* . *------------------------ +/// . . +/// . * +/// . . * | . +/// . . * | . * +/// counter . . * | . * +/// . . * | . * +/// . . * | . * +/// * *---------* +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + let dma = p.GPDMA1_CH1; + let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); + let trigger_pin = p.PA8; + let capture_pin = p.PA10; + + let mut tim = CustomPwmBuilder::new(p.TIM1) + //.frequency(Hertz(123)) + .prescaler_and_period(0, 1337) + .ch1_input( + trigger_pin, + FilterValue::FDTS_DIV32_N8, + InputCaptureMode::BothEdges, + InputTISelection::Normal, + 1, + ) + .trigger_from_ch1(TriggerMode::TriggerMode, TriggerSource::Filtered) + .ch2(out_pin, OutputCompareMode::PwmMode2, 800) + .ch3_input( + capture_pin, + FilterValue::FCK_INT_N2, + InputCaptureMode::Rising, + InputTISelection::Normal, + 0, + ) + .one_pulse_mode() + .finalize(); + + loop { + info!("Manually set pulse width"); + tim.set_compare_value(150, Channel::Ch2); + Timer::after_millis(300).await; + + info!("Send waveform on PA9"); + tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; + + info!("Waiting for rising edge on PA10"); + let capture = tim.wait_for_configured_edge(Channel::Ch3).await; + info!("Rising edge detected on PA10 {} ticks from trigger on PA8", capture); + } +} From 79ad517bfe388391e009331bb12634e7ab3e22c2 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:15:32 +0100 Subject: [PATCH 13/19] cfg out custom timer for l0 --- embassy-stm32/src/timer/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index f36dd5a843..2a8e716f2b 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -7,6 +7,7 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(stm32l0))] pub mod complementary_pwm; +#[cfg(not(stm32l0))] pub mod custom_timer; pub mod input_capture; pub mod low_level; From dd9fbd6cbad1b7f0eb62652e11d2a9c081377b65 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:40:56 +0100 Subject: [PATCH 14/19] Wrap input pins in Peri --- embassy-stm32/src/timer/custom_timer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/custom_timer.rs index 9880cd80ce..e3773114af 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/custom_timer.rs @@ -252,7 +252,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 /// Setup channel 1 as output pub fn etr<#[cfg(afio)] A>( self, - _pin: if_afio!(impl ExternalTriggerPin), + _pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, filter: FilterValue, polarity: Etp, trigger_prescaler: Etps, @@ -282,7 +282,7 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 /// Setup channel 1 as input pub fn ch1_input<#[cfg(afio)] A>( self, - _pin: if_afio!(impl TimerPin), + _pin: Peri<'d, if_afio!(impl TimerPin)>, filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, @@ -313,7 +313,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4 /// Setup channel 2 as input pub fn ch2_input<#[cfg(afio)] A>( self, - _pin: if_afio!(impl TimerPin), + _pin: Peri<'d, if_afio!(impl TimerPin)>, filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, @@ -344,7 +344,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4 /// Setup channel 3 as input pub fn ch3_input<#[cfg(afio)] A>( self, - _pin: if_afio!(impl TimerPin), + _pin: Peri<'d, if_afio!(impl TimerPin)>, filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, @@ -375,7 +375,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 /// Setup channel 3 as input pub fn ch4_input<#[cfg(afio)] A>( self, - _pin: if_afio!(impl TimerPin), + _pin: Peri<'d, if_afio!(impl TimerPin)>, filter: FilterValue, mode: InputCaptureMode, ti_selection: InputTISelection, From 5386db45617d5c76691b0f741377c4c34a7ed027 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:41:37 +0100 Subject: [PATCH 15/19] Fix h5 examples --- examples/stm32h5/src/bin/pwm_custom_etr.rs | 18 ++++++------------ examples/stm32h5/src/bin/pwm_custom_multi.rs | 11 +++++------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/examples/stm32h5/src/bin/pwm_custom_etr.rs b/examples/stm32h5/src/bin/pwm_custom_etr.rs index 564971d9df..1e1bb8f26d 100644 --- a/examples/stm32h5/src/bin/pwm_custom_etr.rs +++ b/examples/stm32h5/src/bin/pwm_custom_etr.rs @@ -3,14 +3,11 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{AfioRemap, OutputType}; use embassy_stm32::pac::timer::vals::{Etp, Etps}; -use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; -use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; -use embassy_stm32::timer::{Channel, UpDma}; +use embassy_stm32::timer::Channel; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode}; +use embassy_stm32::timer::low_level::FilterValue; -use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::{Peri, peripherals}; use {defmt_rtt as _, panic_probe as _}; /// 0 CCR ARR 0 CCR @@ -44,15 +41,12 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let dma = p.DMA1_CH5; - let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); - let trigger_pin = p.PA8; - let capture_pin = p.PA10; + let trigger_pin = p.PA12; - let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) + let mut tim = CustomPwmBuilder::new(p.TIM1) //.frequency(Hertz(123)) .prescaler_and_period(0, 1337) - .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) + .etr(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) .trigger_from_etr(TriggerMode::TriggerMode) .ch1_internal(1234) .one_pulse_mode() diff --git a/examples/stm32h5/src/bin/pwm_custom_multi.rs b/examples/stm32h5/src/bin/pwm_custom_multi.rs index ca7a737b2b..806acac7b1 100644 --- a/examples/stm32h5/src/bin/pwm_custom_multi.rs +++ b/examples/stm32h5/src/bin/pwm_custom_multi.rs @@ -3,14 +3,12 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{AfioRemap, OutputType}; -use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; -use embassy_stm32::timer::{Channel, UpDma}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::{Peri, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -44,7 +42,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let dma = p.GPDMA1_CH1; + //let dma = p.GPDMA1_CH1; let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); let trigger_pin = p.PA8; let capture_pin = p.PA10; @@ -77,7 +75,8 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; info!("Send waveform on PA9"); - tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; + // TODO: Add this once we have embassy-rs/stm32-data#684 + //tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; info!("Waiting for rising edge on PA10"); let capture = tim.wait_for_configured_edge(Channel::Ch3).await; From 07cb77e0c3f2d76955231a1892a75c3eab6f79bf Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 16 Nov 2025 19:41:54 +0100 Subject: [PATCH 16/19] Fix f1 examples --- examples/stm32f1/src/bin/pwm_custom_etr.rs | 17 ++++++----------- examples/stm32f1/src/bin/pwm_custom_multi.rs | 9 ++++----- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_custom_etr.rs b/examples/stm32f1/src/bin/pwm_custom_etr.rs index 564971d9df..f993366210 100644 --- a/examples/stm32f1/src/bin/pwm_custom_etr.rs +++ b/examples/stm32f1/src/bin/pwm_custom_etr.rs @@ -3,14 +3,12 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{AfioRemap, OutputType}; +use embassy_stm32::gpio::AfioRemap; use embassy_stm32::pac::timer::vals::{Etp, Etps}; -use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; -use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; -use embassy_stm32::timer::{Channel, UpDma}; +use embassy_stm32::timer::Channel; +use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode}; +use embassy_stm32::timer::low_level::FilterValue; -use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::{Peri, peripherals}; use {defmt_rtt as _, panic_probe as _}; /// 0 CCR ARR 0 CCR @@ -44,12 +42,9 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let dma = p.DMA1_CH5; - let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); - let trigger_pin = p.PA8; - let capture_pin = p.PA10; + let trigger_pin = p.PA12; - let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(tim) + let mut tim = CustomPwmBuilder::<_, _, _, _, _, _, _>::new(p.TIM1) //.frequency(Hertz(123)) .prescaler_and_period(0, 1337) .etr::>(trigger_pin, FilterValue::FDTS_DIV32_N8, Etp::NOT_INVERTED, Etps::DIV1) diff --git a/examples/stm32f1/src/bin/pwm_custom_multi.rs b/examples/stm32f1/src/bin/pwm_custom_multi.rs index 7b32aed3b3..d412ceb381 100644 --- a/examples/stm32f1/src/bin/pwm_custom_multi.rs +++ b/examples/stm32f1/src/bin/pwm_custom_multi.rs @@ -4,13 +4,11 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{AfioRemap, OutputType}; -use embassy_stm32::pac::timer::vals::{Etp, Etps}; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::custom_timer::{CustomPwmBuilder, TriggerMode, TriggerSource}; use embassy_stm32::timer::low_level::{FilterValue, InputCaptureMode, InputTISelection, OutputCompareMode}; -use embassy_stm32::timer::{Channel, UpDma}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::{Peri, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -44,7 +42,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let dma = p.DMA1_CH5; + let mut dma = p.DMA1_CH5; let out_pin = PwmPin::new(p.PA9, OutputType::PushPull); let trigger_pin = p.PA8; let capture_pin = p.PA10; @@ -77,7 +75,8 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; info!("Send waveform on PA9"); - tim.waveform_up(dma, Channel::Ch1, &[100, 400, 800, 1100, 1200]).await; + tim.waveform_up(dma.reborrow(), Channel::Ch1, &[100, 400, 800, 1100, 1200]) + .await; info!("Waiting for rising edge on PA10"); let capture = tim.wait_for_configured_edge(Channel::Ch3).await; From 64c5b67fa209ba4f9d83f727a25a989fa614a804 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 17 Nov 2025 22:45:54 +0100 Subject: [PATCH 17/19] Remove CustomPwm and use builder for low_level Timer instead --- .../src/timer/{custom_timer.rs => builder.rs} | 140 ++++++------------ embassy-stm32/src/timer/low_level.rs | 2 + embassy-stm32/src/timer/mod.rs | 3 +- 3 files changed, 51 insertions(+), 94 deletions(-) rename embassy-stm32/src/timer/{custom_timer.rs => builder.rs} (76%) diff --git a/embassy-stm32/src/timer/custom_timer.rs b/embassy-stm32/src/timer/builder.rs similarity index 76% rename from embassy-stm32/src/timer/custom_timer.rs rename to embassy-stm32/src/timer/builder.rs index e3773114af..c016898d03 100644 --- a/embassy-stm32/src/timer/custom_timer.rs +++ b/embassy-stm32/src/timer/builder.rs @@ -5,7 +5,7 @@ //! //! The available functionality depends on the timer type. //! -use core::{marker::PhantomData, u16}; +use core::u16; use embassy_hal_internal::Peri; use stm32_metapac::timer::vals::{Etp, Etps, FilterValue, Ts}; @@ -14,7 +14,6 @@ use crate::{ time::Hertz, timer::{ Ch1, Ch2, Ch3, Ch4, CoreInstance, ExternalTriggerPin, GeneralInstance4Channel, TimerPin, - input_capture::InputCaptureFuture, low_level::{CountingMode, InputCaptureMode, InputTISelection, OutputCompareMode, SlaveMode, Timer}, simple_pwm::PwmPin, }, @@ -35,6 +34,8 @@ mod ch_mode { pub struct InternalOutput { pub(crate) duty: u16, } + + // TODO: Look into the capture mode configuration Timer::set_input_capture_mode pub struct Input { pub(crate) filter: FilterValue, pub(crate) mode: InputCaptureMode, @@ -132,7 +133,7 @@ macro_rules! set_field { ) => {{ #[allow(unused_variables)] - let CustomPwmBuilder { + let TimerBuilder { tim, ch1, ch2, @@ -146,7 +147,7 @@ macro_rules! set_field { trig_source, slave_mode, } = $this; - CustomPwmBuilder { + TimerBuilder { tim $(: $tim)*, ch1 $(: $ch1)*, ch2 $(: $ch2)*, @@ -163,8 +164,8 @@ macro_rules! set_field { }; } -/// Used to construct a [CustomPwm] -pub struct CustomPwmBuilder< +/// Used to construct a [Timer] +pub struct TimerBuilder< 'd, T: CoreInstance, CH1: ch_mode::Mode, @@ -191,7 +192,7 @@ pub struct CustomPwmBuilder< } impl<'d, T: CoreInstance> - CustomPwmBuilder< + TimerBuilder< 'd, T, ch_mode::InternalOutput, @@ -222,7 +223,7 @@ impl<'d, T: CoreInstance> } impl<'d, T: CoreInstance, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS> + TimerBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS> { /// Set manually frequency by specifying prescaler and period pub fn prescaler_and_period(mut self, prescaler: u16, period_ticks: u32) -> Self { @@ -247,7 +248,7 @@ impl<'d, T: CoreInstance, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode:: } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, TS> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS> + TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS> { /// Setup channel 1 as output pub fn etr<#[cfg(afio)] A>( @@ -256,13 +257,13 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 filter: FilterValue, polarity: Etp, trigger_prescaler: Etps, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS> { + ) -> TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS> { set_field!(self, etr: external_trigger::Etr{filter, polarity, trigger_prescaler }) } } impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - CustomPwmBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS> + TimerBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS> { /// Set ch1 to be used as internal output, can be used as time base etc pub fn ch1_internal(self, duty: u16) -> Self { @@ -275,7 +276,7 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 _pin: if_afio!(PwmPin<'d, T, Ch1, A>), mode: OutputCompareMode, duty: u16, - ) -> CustomPwmBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS> { set_field!(self, ch1: ch_mode::Output { mode, duty }) } @@ -287,13 +288,13 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS> { set_field!(self, ch1: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - CustomPwmBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS> + TimerBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS> { /// Set ch2 to be used as internal output, can be used as time base etc pub fn ch2_internal(self, duty: u16) -> Self { @@ -306,7 +307,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4 _pin: if_afio!(PwmPin<'d, T, Ch2, A>), mode: OutputCompareMode, duty: u16, - ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS> { set_field!(self, ch2: ch_mode::Output { mode, duty }) } @@ -318,13 +319,13 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4 mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS> { set_field!(self, ch2: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS> + TimerBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS> { /// Set ch3 to be used as internal output, can be used as time base etc pub fn ch3_internal(self, duty: u16) -> Self { @@ -337,7 +338,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4 _pin: if_afio!(PwmPin<'d, T, Ch3, A>), mode: OutputCompareMode, duty: u16, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS> { set_field!(self, ch3: ch_mode::Output { mode, duty }) } @@ -349,13 +350,13 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4 mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS> { set_field!(self, ch3: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, ETR, TS> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> + TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> { /// Set ch4 to be used as internal output, can be used as time base etc pub fn ch4_internal(self, duty: u16) -> Self { @@ -368,7 +369,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 _pin: if_afio!(PwmPin<'d, T, Ch4, A>), mode: OutputCompareMode, duty: u16, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS> { set_field!(self, ch4: ch_mode::Output { mode, duty }) } @@ -380,7 +381,7 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS> { + ) -> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS> { set_field!(self, ch4: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) } } @@ -407,13 +408,13 @@ pub enum TriggerSource { } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> - CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal> + TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_etr( self, mode: TriggerMode, - ) -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr> { + ) -> TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr> { set_field!(self, trigger_source: trigger_source::Etr, trig_source: Ts::TI1F_ED, slave_mode: match mode { @@ -427,14 +428,14 @@ impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3 } impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> - CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal> + TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch1( self, mode: TriggerMode, source: TriggerSource, - ) -> CustomPwmBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1> { + ) -> TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1> { set_field!(self, trigger_source: trigger_source::Ch1, trig_source: match source { TriggerSource::EdgeDetector => Ts::TI1F_ED, @@ -451,13 +452,13 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 } impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> - CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal> + TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal> { /// Setup timer to be triggered from ch1 compare match event pub fn trigger_from_ch2( self, mode: TriggerMode, - ) -> CustomPwmBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2> { + ) -> TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2> { set_field!(self, trigger_source: trigger_source::Ch2, trig_source: Ts::TI2FP2, slave_mode: match mode { TriggerMode::ResetMode => SlaveMode::RESET_MODE, TriggerMode::GatedMode => SlaveMode::GATED_MODE, @@ -475,81 +476,36 @@ impl< CH3: ch_mode::Mode, ETR: external_trigger::Trigger, TS, -> CustomPwmBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> +> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> { /// Finalize configuration and create the [CustomPwm] - pub fn finalize(self) -> CustomPwm<'d, T> { + pub fn finalize(self) -> Timer<'d, T> { use ch_mode::Mode; - let mut inner = Timer::new(self.tim); + let mut timer = Timer::new(self.tim); - self.ch1.init(super::Channel::Ch1, &mut inner); - self.ch2.init(super::Channel::Ch2, &mut inner); - self.ch3.init(super::Channel::Ch3, &mut inner); - self.ch4.init(super::Channel::Ch4, &mut inner); - self.etr.init(&mut inner); + self.ch1.init(super::Channel::Ch1, &mut timer); + self.ch2.init(super::Channel::Ch2, &mut timer); + self.ch3.init(super::Channel::Ch3, &mut timer); + self.ch4.init(super::Channel::Ch4, &mut timer); + self.etr.init(&mut timer); - inner.set_trigger_source(self.trig_source); - inner.set_slave_mode(self.slave_mode); + timer.set_trigger_source(self.trig_source); + timer.set_slave_mode(self.slave_mode); - inner.set_counting_mode(self.counting_mode); + timer.set_counting_mode(self.counting_mode); match self.speed { - Speed::Hertz(hz) => inner.set_frequency(hz), + Speed::Hertz(hz) => timer.set_frequency(hz), Speed::Manual { arr, psc } => { - inner.set_max_compare_value(arr); - inner.set_prescaler(psc); + timer.set_max_compare_value(arr); + timer.set_prescaler(psc); } } - inner.generate_update_event(); - - inner.regs_core().cr1().modify(|r| r.set_opm(self.one_pulse_mode)); - inner.enable_outputs(); - inner.start(); - - CustomPwm { inner } - } -} - -/// Use [CustomPwmBuilder::new] to create a new timer -pub struct CustomPwm<'d, T: CoreInstance> { - pub(crate) inner: Timer<'d, T>, -} - -impl<'d, T: GeneralInstance4Channel> CustomPwm<'d, T> { - /// Set compare value - pub fn set_compare_value(&mut self, duty: u16, channel: super::Channel) { - self.inner.set_compare_value(channel, duty as u32); - } - - /// Generate a sequence of PWM waveform - /// - /// Note: - /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: super::Channel, duty: &[u16]) { - self.inner.waveform_up(dma, channel, duty).await; - } - - /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: super::Channel) -> u32 { - self.inner.get_capture_value(channel) - } - - fn new_future(&self, channel: super::Channel) -> InputCaptureFuture { - self.inner.enable_channel(channel, true); - self.inner.enable_input_interrupt(channel, true); - - InputCaptureFuture { - channel, - phantom: PhantomData, - } - } + timer.generate_update_event(); - /// Asynchronously wait until the pin sees an edge as configured on timer init. - pub async fn wait_for_configured_edge(&mut self, channel: super::Channel) -> u32 { - self.new_future(channel).await - } + timer.regs_core().cr1().modify(|r| r.set_opm(self.one_pulse_mode)); + timer.enable_outputs(); + timer.start(); - /// Asynchronously wait until the period event - pub async fn wait_for_period(&mut self) -> u32 { - todo!() + timer } } diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 5246282c2f..ac9dfff2ec 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -241,6 +241,8 @@ impl<'d, T: CoreInstance> Drop for Timer<'d, T> { impl<'d, T: CoreInstance> Timer<'d, T> { /// Create a new timer driver. + /// + /// Consider using [super::builder::TimerBuilder] instead pub fn new(tim: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 2a8e716f2b..68e7e66cf6 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -5,10 +5,9 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; +pub mod builder; #[cfg(not(stm32l0))] pub mod complementary_pwm; -#[cfg(not(stm32l0))] -pub mod custom_timer; pub mod input_capture; pub mod low_level; pub mod one_pulse; From 46a85433efdf3ca430e5475ffb322cf1cdd7d841 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 17 Nov 2025 23:18:23 +0100 Subject: [PATCH 18/19] Remove all my beautiful types --- embassy-stm32/src/timer/builder.rs | 482 +++++++++-------------------- 1 file changed, 141 insertions(+), 341 deletions(-) diff --git a/embassy-stm32/src/timer/builder.rs b/embassy-stm32/src/timer/builder.rs index c016898d03..a4f0e50cda 100644 --- a/embassy-stm32/src/timer/builder.rs +++ b/embassy-stm32/src/timer/builder.rs @@ -13,99 +13,81 @@ use stm32_metapac::timer::vals::{Etp, Etps, FilterValue, Ts}; use crate::{ time::Hertz, timer::{ - Ch1, Ch2, Ch3, Ch4, CoreInstance, ExternalTriggerPin, GeneralInstance4Channel, TimerPin, + self, Ch1, CoreInstance, ExternalTriggerPin, GeneralInstance4Channel, TimerPin, low_level::{CountingMode, InputCaptureMode, InputTISelection, OutputCompareMode, SlaveMode, Timer}, simple_pwm::PwmPin, }, }; -mod ch_mode { - use crate::timer::{ - Channel, - low_level::{InputCaptureMode, InputTISelection}, - }; - - use super::*; - - pub trait Mode { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>); - } - - pub struct InternalOutput { - pub(crate) duty: u16, - } - +enum ChannelMode { + InternalOutput { + duty: u16, + }, // TODO: Look into the capture mode configuration Timer::set_input_capture_mode - pub struct Input { - pub(crate) filter: FilterValue, - pub(crate) mode: InputCaptureMode, - pub(crate) ti_selection: InputTISelection, - pub(crate) prescaler_factor: u8, - } - pub struct Output { - pub(crate) mode: OutputCompareMode, - pub(crate) duty: u16, - } - - impl Mode for InternalOutput { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { - tim.set_output_compare_mode(channel, OutputCompareMode::Frozen); - tim.set_compare_value(channel, self.duty as u32) - } - } - - impl Mode for Input { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { - tim.set_input_capture_filter(channel, self.filter); - tim.set_input_capture_mode(channel, self.mode); - tim.set_input_capture_prescaler(channel, self.prescaler_factor); - tim.set_input_ti_selection(channel, self.ti_selection); - } - } + Input { + filter: FilterValue, + mode: InputCaptureMode, + ti_selection: InputTISelection, + prescaler_factor: u8, + }, + Output { + mode: OutputCompareMode, + duty: u16, + }, +} - impl Mode for Output { - fn init(self, channel: Channel, tim: &mut Timer<'_, T>) { - tim.set_output_compare_mode(channel, self.mode); - tim.set_compare_value(channel, self.duty as u32) +impl ChannelMode { + fn init(self, channel: timer::Channel, tim: &mut Timer<'_, T>) { + match self { + ChannelMode::InternalOutput { duty } => { + tim.set_output_compare_mode(channel, OutputCompareMode::Frozen); + tim.set_compare_value(channel, duty as u32) + } + ChannelMode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + } => { + tim.set_input_capture_filter(channel, filter); + tim.set_input_capture_mode(channel, mode); + tim.set_input_capture_prescaler(channel, prescaler_factor); + tim.set_input_ti_selection(channel, ti_selection); + } + ChannelMode::Output { mode, duty } => { + tim.set_output_compare_mode(channel, mode); + tim.set_compare_value(channel, duty as u32) + } } } } -mod trigger_source { - pub struct Internal; - pub struct Ch1; - pub struct Ch2; - pub struct Etr; +enum ExternalTrigger { + Unused, + Etr { + filter: FilterValue, + polarity: Etp, + trigger_prescaler: Etps, + }, } -mod external_trigger { - use stm32_metapac::timer::vals::{Etp, Etps}; - - use super::*; - - pub trait Trigger { - fn init(self, tim: &mut Timer<'_, T>); - } - pub struct Unused; - pub struct Etr { - pub(crate) filter: FilterValue, - pub(crate) polarity: Etp, - pub(crate) trigger_prescaler: Etps, - } - - impl Trigger for Unused { - fn init(self, _tim: &mut Timer<'_, T>) {} - } - - impl Trigger for Etr { - fn init(self, tim: &mut Timer<'_, T>) { - tim.regs_gp16().af1().modify(|w| w.set_etrsel(0)); // 0: ETR input - tim.regs_gp16().smcr().modify(|w| { - w.set_etf(self.filter); - w.set_ece(false); // <--- TODO: I really need to look into how to set this and the SMS bits - w.set_etp(self.polarity); - w.set_etps(self.trigger_prescaler); - }); +impl ExternalTrigger { + fn init(self, tim: &mut Timer<'_, T>) { + match self { + ExternalTrigger::Unused => (), + ExternalTrigger::Etr { + filter, + polarity, + trigger_prescaler, + } => { + tim.regs_gp16().af1().modify(|w| w.set_etrsel(0)); // 0: ETR input + tim.regs_gp16().smcr().modify(|w| { + w.set_etf(filter); + w.set_ece(false); // <--- TODO: I really need to look into how to set this and the SMS bits + w.set_etp(polarity); + w.set_etps(trigger_prescaler); + }); + } } } } @@ -115,74 +97,14 @@ enum Speed { Manual { arr: u32, psc: u16 }, } -macro_rules! set_field { - ( - $this:expr - $(, tim: $tim:expr)* - $(, ch1: $ch1:expr)* - $(, ch2: $ch2:expr)* - $(, ch3: $ch3:expr)* - $(, ch4: $ch4:expr)* - $(, etr: $etr:expr)* - $(, one_pulse_mode: $one_pulse_mode:expr)* - $(, counting_mode: $counting_mode:expr)* - $(, speed: $speed:expr)* - $(, trigger_source: $trigger_source:expr)* - $(, trig_source: $trig_source:expr)* - $(, slave_mode: $slave_mode:expr)* - - ) => {{ - #[allow(unused_variables)] - let TimerBuilder { - tim, - ch1, - ch2, - ch3, - ch4, - etr, - one_pulse_mode, - counting_mode, - speed, - trigger_source, - trig_source, - slave_mode, - } = $this; - TimerBuilder { - tim $(: $tim)*, - ch1 $(: $ch1)*, - ch2 $(: $ch2)*, - ch3 $(: $ch3)*, - ch4 $(: $ch4)*, - etr $(: $etr)*, - one_pulse_mode $(: $one_pulse_mode)*, - counting_mode $(: $counting_mode)*, - speed $(: $speed)*, - trigger_source $(: $trigger_source)*, - trig_source $(: $trig_source)*, - slave_mode $(: $slave_mode)*, - }} - }; -} - /// Used to construct a [Timer] -pub struct TimerBuilder< - 'd, - T: CoreInstance, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - CH4: ch_mode::Mode, - ETR, - TS, -> { +pub struct TimerBuilder<'d, T: CoreInstance> { tim: Peri<'d, T>, - ch1: CH1, - ch2: CH2, - ch3: CH3, - ch4: CH4, - etr: ETR, - - trigger_source: TS, + ch1: ChannelMode, + ch2: ChannelMode, + ch3: ChannelMode, + ch4: ChannelMode, + etr: ExternalTrigger, trig_source: Ts, slave_mode: SlaveMode, @@ -191,40 +113,26 @@ pub struct TimerBuilder< speed: Speed, } -impl<'d, T: CoreInstance> - TimerBuilder< - 'd, - T, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - ch_mode::InternalOutput, - external_trigger::Unused, - trigger_source::Internal, - > -{ +impl<'d, T: CoreInstance> TimerBuilder<'d, T> { /// Construct a [CustomPwmBuilder] which can be used to construct a [CustomPwm] pub fn new(tim: Peri<'d, T>) -> Self { Self { tim, - ch1: ch_mode::InternalOutput { duty: 0 }, - ch2: ch_mode::InternalOutput { duty: 0 }, - ch3: ch_mode::InternalOutput { duty: 0 }, - ch4: ch_mode::InternalOutput { duty: 0 }, - etr: external_trigger::Unused, + ch1: ChannelMode::InternalOutput { duty: 0 }, + ch2: ChannelMode::InternalOutput { duty: 0 }, + ch3: ChannelMode::InternalOutput { duty: 0 }, + ch4: ChannelMode::InternalOutput { duty: 0 }, + etr: ExternalTrigger::Unused, one_pulse_mode: false, counting_mode: CountingMode::EdgeAlignedUp, speed: Speed::Manual { arr: u32::MAX, psc: 0 }, - trigger_source: trigger_source::Internal, trig_source: Ts::ITR0, slave_mode: SlaveMode::DISABLED, } } } -impl<'d, T: CoreInstance, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - TimerBuilder<'d, T, CH1, CH2, CH3, CH4, ETR, TS> -{ +impl<'d, T: CoreInstance> TimerBuilder<'d, T> { /// Set manually frequency by specifying prescaler and period pub fn prescaler_and_period(mut self, prescaler: u16, period_ticks: u32) -> Self { self.speed = Speed::Manual { @@ -247,37 +155,60 @@ impl<'d, T: CoreInstance, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode:: } } -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, TS> - TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Unused, TS> -{ +impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Setup channel 1 as output pub fn etr<#[cfg(afio)] A>( - self, + mut self, _pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, filter: FilterValue, polarity: Etp, trigger_prescaler: Etps, - ) -> TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, TS> { - set_field!(self, etr: external_trigger::Etr{filter, polarity, trigger_prescaler }) + ) -> Self { + self.etr = ExternalTrigger::Etr { + filter, + polarity, + trigger_prescaler, + }; + self + } + + /// Setup timer to be triggered from ch1 compare match event + pub fn etr_as_trigger<#[cfg(afio)] A>( + self, + pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, + filter: FilterValue, + polarity: Etp, + trigger_prescaler: Etps, + mode: TriggerMode, + ) -> Self { + let mut s = self.etr(pin, filter, polarity, trigger_prescaler); + s.trig_source = Ts::TI1F_ED; + s.slave_mode = match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }; + s } } -impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - TimerBuilder<'d, T, ch_mode::InternalOutput, CH2, CH3, CH4, ETR, TS> -{ +impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Set ch1 to be used as internal output, can be used as time base etc - pub fn ch1_internal(self, duty: u16) -> Self { - set_field!(self, ch1: ch_mode::InternalOutput { duty }) + pub fn ch1_internal(mut self, duty: u16) -> Self { + self.ch1 = ChannelMode::InternalOutput { duty }; + self } /// Setup channel 1 as output pub fn ch1<#[cfg(afio)] A>( - self, + mut self, _pin: if_afio!(PwmPin<'d, T, Ch1, A>), mode: OutputCompareMode, duty: u16, - ) -> TimerBuilder<'d, T, ch_mode::Output, CH2, CH3, CH4, ETR, TS> { - set_field!(self, ch1: ch_mode::Output { mode, duty }) + ) -> Self { + self.ch1 = ChannelMode::Output { mode, duty }; + self } /// Setup channel 1 as input @@ -288,101 +219,41 @@ impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4 mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, TS> { - set_field!(self, ch1: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) - } -} - -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - TimerBuilder<'d, T, CH1, ch_mode::InternalOutput, CH3, CH4, ETR, TS> -{ - /// Set ch2 to be used as internal output, can be used as time base etc - pub fn ch2_internal(self, duty: u16) -> Self { - set_field!(self, ch2: ch_mode::InternalOutput { duty }) - } - - /// Setup channel 2 as output - pub fn ch2<#[cfg(afio)] A>( - self, - _pin: if_afio!(PwmPin<'d, T, Ch2, A>), - mode: OutputCompareMode, - duty: u16, - ) -> TimerBuilder<'d, T, CH1, ch_mode::Output, CH3, CH4, ETR, TS> { - set_field!(self, ch2: ch_mode::Output { mode, duty }) - } - - /// Setup channel 2 as input - pub fn ch2_input<#[cfg(afio)] A>( - self, - _pin: Peri<'d, if_afio!(impl TimerPin)>, - filter: FilterValue, - mode: InputCaptureMode, - ti_selection: InputTISelection, - prescaler_factor: u8, - ) -> TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, TS> { - set_field!(self, ch2: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) - } -} - -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH4: ch_mode::Mode, ETR, TS> - TimerBuilder<'d, T, CH1, CH2, ch_mode::InternalOutput, CH4, ETR, TS> -{ - /// Set ch3 to be used as internal output, can be used as time base etc - pub fn ch3_internal(self, duty: u16) -> Self { - set_field!(self, ch3: ch_mode::InternalOutput { duty }) - } - - /// Setup channel 3 as output - pub fn ch3<#[cfg(afio)] A>( - self, - _pin: if_afio!(PwmPin<'d, T, Ch3, A>), - mode: OutputCompareMode, - duty: u16, - ) -> TimerBuilder<'d, T, CH1, CH2, ch_mode::Output, CH4, ETR, TS> { - set_field!(self, ch3: ch_mode::Output { mode, duty }) + ) -> Self { + let mut s = self; + s.ch1 = ChannelMode::Input { + filter, + mode, + ti_selection, + prescaler_factor, + }; + s } - /// Setup channel 3 as input - pub fn ch3_input<#[cfg(afio)] A>( + /// Setup channel 1 as input and set this as this timers trigger source + pub fn ch1_input_as_trigger<#[cfg(afio)] A>( self, - _pin: Peri<'d, if_afio!(impl TimerPin)>, + pin: Peri<'d, if_afio!(impl TimerPin)>, filter: FilterValue, - mode: InputCaptureMode, + capture_mode: InputCaptureMode, ti_selection: InputTISelection, prescaler_factor: u8, - ) -> TimerBuilder<'d, T, CH1, CH2, ch_mode::Input, CH4, ETR, TS> { - set_field!(self, ch3: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) - } -} - -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, ETR, TS> - TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> -{ - /// Set ch4 to be used as internal output, can be used as time base etc - pub fn ch4_internal(self, duty: u16) -> Self { - set_field!(self, ch4: ch_mode::InternalOutput { duty }) - } - - /// Setup channel 4 as output - pub fn ch4<#[cfg(afio)] A>( - self, - _pin: if_afio!(PwmPin<'d, T, Ch4, A>), - mode: OutputCompareMode, - duty: u16, - ) -> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::Output, ETR, TS> { - set_field!(self, ch4: ch_mode::Output { mode, duty }) - } + mode: TriggerMode, + source: TriggerSource, + ) -> Self { + let mut s = self.ch1_input(pin, filter, capture_mode, ti_selection, prescaler_factor); + s.trig_source = match source { + TriggerSource::EdgeDetector => Ts::TI1F_ED, + TriggerSource::Filtered => Ts::TI1FP1, + }; + s.slave_mode = match mode { + TriggerMode::ResetMode => SlaveMode::RESET_MODE, + TriggerMode::GatedMode => SlaveMode::GATED_MODE, + TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, + TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, + }; - /// Setup channel 3 as input - pub fn ch4_input<#[cfg(afio)] A>( - self, - _pin: Peri<'d, if_afio!(impl TimerPin)>, - filter: FilterValue, - mode: InputCaptureMode, - ti_selection: InputTISelection, - prescaler_factor: u8, - ) -> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::Input, ETR, TS> { - set_field!(self, ch4: ch_mode::Input { filter, mode, ti_selection, prescaler_factor }) + s } } @@ -407,80 +278,9 @@ pub enum TriggerSource { Filtered, } -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode> - TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Internal> -{ - /// Setup timer to be triggered from ch1 compare match event - pub fn trigger_from_etr( - self, - mode: TriggerMode, - ) -> TimerBuilder<'d, T, CH1, CH2, CH3, CH4, external_trigger::Etr, trigger_source::Etr> { - set_field!(self, trigger_source: trigger_source::Etr, - trig_source: Ts::TI1F_ED, - slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - } - ) - } -} - -impl<'d, T: GeneralInstance4Channel, CH2: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> - TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Internal> -{ - /// Setup timer to be triggered from ch1 compare match event - pub fn trigger_from_ch1( - self, - mode: TriggerMode, - source: TriggerSource, - ) -> TimerBuilder<'d, T, ch_mode::Input, CH2, CH3, CH4, ETR, trigger_source::Ch1> { - set_field!(self, trigger_source: trigger_source::Ch1, - trig_source: match source { - TriggerSource::EdgeDetector => Ts::TI1F_ED, - TriggerSource::Filtered => Ts::TI1FP1, - }, - slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - } - ) - } -} - -impl<'d, T: GeneralInstance4Channel, CH1: ch_mode::Mode, CH3: ch_mode::Mode, CH4: ch_mode::Mode, ETR> - TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Internal> -{ - /// Setup timer to be triggered from ch1 compare match event - pub fn trigger_from_ch2( - self, - mode: TriggerMode, - ) -> TimerBuilder<'d, T, CH1, ch_mode::Input, CH3, CH4, ETR, trigger_source::Ch2> { - set_field!(self, trigger_source: trigger_source::Ch2, trig_source: Ts::TI2FP2, slave_mode: match mode { - TriggerMode::ResetMode => SlaveMode::RESET_MODE, - TriggerMode::GatedMode => SlaveMode::GATED_MODE, - TriggerMode::TriggerMode => SlaveMode::TRIGGER_MODE, - TriggerMode::ExternalClockMode => SlaveMode::EXT_CLOCK_MODE, - }) - } -} - -impl< - 'd, - T: GeneralInstance4Channel, - CH1: ch_mode::Mode, - CH2: ch_mode::Mode, - CH3: ch_mode::Mode, - ETR: external_trigger::Trigger, - TS, -> TimerBuilder<'d, T, CH1, CH2, CH3, ch_mode::InternalOutput, ETR, TS> -{ +impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Finalize configuration and create the [CustomPwm] pub fn finalize(self) -> Timer<'d, T> { - use ch_mode::Mode; let mut timer = Timer::new(self.tim); self.ch1.init(super::Channel::Ch1, &mut timer); From 0b0791dea0da7773b9718acb846d0147eb216c61 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 17 Nov 2025 23:37:51 +0100 Subject: [PATCH 19/19] combine impl blocks --- embassy-stm32/src/timer/builder.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/embassy-stm32/src/timer/builder.rs b/embassy-stm32/src/timer/builder.rs index a4f0e50cda..8f95ba03ad 100644 --- a/embassy-stm32/src/timer/builder.rs +++ b/embassy-stm32/src/timer/builder.rs @@ -113,7 +113,7 @@ pub struct TimerBuilder<'d, T: CoreInstance> { speed: Speed, } -impl<'d, T: CoreInstance> TimerBuilder<'d, T> { +impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Construct a [CustomPwmBuilder] which can be used to construct a [CustomPwm] pub fn new(tim: Peri<'d, T>) -> Self { Self { @@ -130,9 +130,7 @@ impl<'d, T: CoreInstance> TimerBuilder<'d, T> { slave_mode: SlaveMode::DISABLED, } } -} -impl<'d, T: CoreInstance> TimerBuilder<'d, T> { /// Set manually frequency by specifying prescaler and period pub fn prescaler_and_period(mut self, prescaler: u16, period_ticks: u32) -> Self { self.speed = Speed::Manual { @@ -153,9 +151,7 @@ impl<'d, T: CoreInstance> TimerBuilder<'d, T> { self.one_pulse_mode = true; self } -} -impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Setup channel 1 as output pub fn etr<#[cfg(afio)] A>( mut self, @@ -191,9 +187,7 @@ impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { }; s } -} -impl<'d, T: GeneralInstance4Channel> TimerBuilder<'d, T> { /// Set ch1 to be used as internal output, can be used as time base etc pub fn ch1_internal(mut self, duty: u16) -> Self { self.ch1 = ChannelMode::InternalOutput { duty };