From 790940a18c093fb7c7f94585be81d7d02892b4fb Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 27 Nov 2025 11:29:24 -0600 Subject: stm32: use typelevel timer type --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/fmt.rs | 10 ++ embassy-stm32/src/timer/complementary_pwm.rs | 18 +-- embassy-stm32/src/timer/input_capture.rs | 2 +- embassy-stm32/src/timer/low_level.rs | 191 ++++++++++----------------- embassy-stm32/src/timer/mod.rs | 30 +++-- embassy-stm32/src/timer/one_pulse.rs | 12 +- embassy-stm32/src/timer/pwm_input.rs | 14 +- embassy-stm32/src/timer/ringbuffered.rs | 12 +- embassy-stm32/src/timer/simple_pwm.rs | 16 +-- 10 files changed, 138 insertions(+), 168 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 4c38b0add..13c95b60a 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: stm32: use typelevel timer type to allow dma for 32 bit timers - fix: fix incorrect handling of split interrupts in timer driver - feat: allow granular stop for regular usart - feat: Add continuous waveform method to SimplePWM diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index b6ae24ee8..b731796f0 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -206,6 +206,16 @@ macro_rules! error { }; } +#[cfg(feature = "defmt")] +trait_set::trait_set! { + pub trait Debuggable = Debug + defmt::Format; +} + +#[cfg(not(feature = "defmt"))] +trait_set::trait_set! { + pub trait Debuggable = Debug; +} + #[cfg(feature = "defmt")] #[collapse_debuginfo(yes)] macro_rules! unwrap { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 77f19a37b..d194899c6 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -178,9 +178,9 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn get_max_duty(&self) -> u16 { if self.inner.get_counting_mode().is_center_aligned() { - self.inner.get_max_compare_value() as u16 + unwrap!(self.inner.get_max_compare_value().try_into()) } else { - self.inner.get_max_compare_value() as u16 + 1 + unwrap!(self.inner.get_max_compare_value().try_into()) + 1 } } @@ -189,7 +189,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty as _) + self.inner.set_compare_value(channel, duty.into()) } /// Set the output polarity for a given channel. @@ -220,7 +220,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// 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: Channel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { self.inner.enable_channel(channel, true); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; @@ -261,7 +261,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[u16], + duty: &[T::Word], ) { self.inner.enable_update_dma(true); self.inner @@ -291,20 +291,20 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) as u16 + unwrap!(self.inner.get_compare_value(channel).try_into()) } fn get_max_duty(&self) -> Self::Duty { if self.inner.get_counting_mode().is_center_aligned() { - self.inner.get_max_compare_value() as u16 + unwrap!(self.inner.get_max_compare_value().try_into()) } else { - self.inner.get_max_compare_value() as u16 + 1 + unwrap!(self.inner.get_max_compare_value().try_into()) + 1 } } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty as u32) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } fn set_period

(&mut self, period: P) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 9cf0f8c34..3e1482b67 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -97,7 +97,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Get capture value for a channel. pub fn get_capture_value(&self, channel: Channel) -> u32 { - self.inner.get_capture_value(channel) + self.inner.get_capture_value(channel).into() } /// Get input interrupt. diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index aba08081f..73a81bff1 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -268,6 +268,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> { unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } } + #[cfg(stm32l0)] + fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } + } + /// Start the timer. pub fn start(&self) { self.regs_core().cr1().modify(|r| r.set_cen(true)); @@ -296,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// get the capability of the timer pub fn bits(&self) -> TimerBits { - T::BITS + match T::Word::bits() { + 16 => TimerBits::Bits16, + #[cfg(not(stm32l0))] + 32 => TimerBits::Bits32, + _ => unreachable!(), + } } /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. @@ -306,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved /// because it needs to count up and down. pub fn set_frequency(&self, frequency: Hertz) { - match T::BITS { - TimerBits::Bits16 => { - self.set_frequency_internal(frequency, 16); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.set_frequency_internal(frequency, 32); - } - } + self.set_frequency_internal(frequency, T::Word::bits()); } - pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { + pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) { let f = frequency.0; assert!(f > 0); let timer_f = T::frequency().0; @@ -326,25 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); - match T::BITS { - TimerBits::Bits16 => { - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr = unwrap!(u16::try_from(divide_by - 1)); - - let regs = self.regs_core(); - regs.psc().write_value(psc); - regs.arr().write(|r| r.set_arr(arr)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); + // the timer counts `0..=arr`, we want it to count `0..divide_by` + let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1)); - let regs = self.regs_gp32_unchecked(); - regs.psc().write_value(psc); - regs.arr().write_value(arr); - } - } + let regs = self.regs_gp32_unchecked(); + regs.psc().write_value(psc); + #[cfg(stm32l0)] + regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); + #[cfg(not(stm32l0))] + regs.arr().write_value(arr.into()); } /// Set tick frequency. @@ -393,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> { pub fn get_frequency(&self) -> Hertz { let timer_f = T::frequency(); - match T::BITS { - TimerBits::Bits16 => { - let regs = self.regs_core(); - let arr = regs.arr().read().arr(); - let psc = regs.psc().read(); + let regs = self.regs_gp32_unchecked(); + #[cfg(not(stm32l0))] + let arr = regs.arr().read(); + #[cfg(stm32l0)] + let arr = regs.arr().read().arr(); + let psc = regs.psc().read(); - timer_f / arr / (psc + 1) - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let regs = self.regs_gp32_unchecked(); - let arr = regs.arr().read(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } - } + timer_f / arr / (psc + 1) } /// Get the clock frequency of the timer (before prescaler is applied). @@ -469,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { } /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - pub fn get_max_compare_value(&self) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), - } + pub fn get_max_compare_value(&self) -> T::Word { + #[cfg(not(stm32l0))] + return unwrap!(self.regs_gp32_unchecked().arr().read().try_into()); + #[cfg(stm32l0)] + return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into()); } /// Set the max compare value. /// /// An update event is generated to load the new value. The update event is /// generated such that it will not cause an interrupt or DMA request. - pub fn set_max_compare_value(&self, ticks: u32) { - match T::BITS { - TimerBits::Bits16 => { - let arr = unwrap!(u16::try_from(ticks)); + pub fn set_max_compare_value(&self, ticks: T::Word) { + let arr = ticks; - let regs = self.regs_1ch(); - regs.arr().write(|r| r.set_arr(arr)); + let regs = self.regs_gp32_unchecked(); + #[cfg(not(stm32l0))] + regs.arr().write_value(arr.into()); + #[cfg(stm32l0)] + regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let arr = ticks; - - let regs = self.regs_gp32_unchecked(); - regs.arr().write_value(arr); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); - } - } + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); } } @@ -638,26 +608,23 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Set compare value for a channel. - pub fn set_compare_value(&self, channel: Channel, value: u32) { - match T::BITS { - TimerBits::Bits16 => { - let value = unwrap!(u16::try_from(value)); - self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); - } - } + pub fn set_compare_value(&self, channel: Channel, value: T::Word) { + #[cfg(not(stm32l0))] + self.regs_gp32_unchecked() + .ccr(channel.index()) + .write_value(value.into()); + #[cfg(stm32l0)] + self.regs_gp16() + .ccr(channel.index()) + .modify(|w| w.set_ccr(unwrap!(value.try_into()))); } /// Get compare value for a channel. - pub fn get_compare_value(&self, channel: Channel) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), - } + pub fn get_compare_value(&self, channel: Channel) -> T::Word { + #[cfg(not(stm32l0))] + return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into()); + #[cfg(stm32l0)] + return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); } /// Setup a ring buffer for the channel @@ -665,8 +632,8 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - dma_buf: &'a mut [u16], - ) -> WritableRingBuffer<'a, u16> { + dma_buf: &'a mut [T::Word], + ) -> WritableRingBuffer<'a, T::Word> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -686,7 +653,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { WritableRingBuffer::new( dma, req, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut T::Word, dma_buf, dma_transfer_option, ) @@ -701,7 +668,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - duty: &'a [u16], + duty: &'a [T::Word], ) -> Transfer<'a> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -719,29 +686,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { ..Default::default() }; - match self.bits() { - TimerBits::Bits16 => Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ), - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - } - } + Transfer::new_write( + dma, + req, + duty, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut T::Word, + dma_transfer_option, + ) } } @@ -779,7 +730,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma: Peri<'a, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &'a [u16], + duty: &'a [T::Word], ) -> Transfer<'a> { let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; let start_ch_index = starting_channel.index(); @@ -815,14 +766,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().dmar().as_ptr() as *mut u16, + self.regs_gp16().dmar().as_ptr() as *mut T::Word, dma_transfer_option, ) } } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { + pub fn get_capture_value(&self, channel: Channel) -> T::Word { self.get_compare_value(channel) } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 3fa363881..74d1c9e77 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -15,6 +15,8 @@ pub mod qei; pub mod ringbuffered; pub mod simple_pwm; +use crate::dma::word::Word; +use crate::fmt::Debuggable; use crate::interrupt; use crate::rcc::RccPeripheral; @@ -163,7 +165,12 @@ pub trait CoreInstance: SealedInstance + 'static { type UpdateInterrupt: interrupt::typelevel::Interrupt; /// Amount of bits this timer has. - const BITS: TimerBits; + type Word: Word + + TryInto + + From + + TryFrom + + Into + + TryFrom; /// Registers for this timer. /// @@ -241,7 +248,7 @@ dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); #[allow(unused)] macro_rules! impl_core_timer { - ($inst:ident, $bits:expr) => { + ($inst:ident, $bits:ident) => { impl SealedInstance for crate::peripherals::$inst { fn state() -> &'static State { static STATE: State = State::new(); @@ -251,8 +258,7 @@ macro_rules! impl_core_timer { impl CoreInstance for crate::peripherals::$inst { type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; - - const BITS: TimerBits = $bits; + type Word = $bits; fn regs() -> *mut () { crate::pac::$inst.as_ptr() @@ -306,13 +312,13 @@ macro_rules! impl_general_4ch_blank_sealed { foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -322,7 +328,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -332,7 +338,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -342,7 +348,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits32); + impl_core_timer!($inst, u32); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -353,7 +359,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -366,7 +372,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); @@ -379,7 +385,7 @@ foreach_interrupt! { }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); + impl_core_timer!($inst, u16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} impl_general_1ch!($inst); diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index fe8681356..989e1d630 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -199,7 +199,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { self.inner.set_counting_mode(counting_mode); self.inner.set_tick_freq(freq); - self.inner.set_max_compare_value(pulse_end); + self.inner.set_max_compare_value(unwrap!(pulse_end.try_into())); self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); // Required for advanced timers, see GeneralInstance4Channel for details self.inner.enable_outputs(); @@ -211,14 +211,14 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } /// Set the end of the pulse in ticks from the trigger. pub fn set_pulse_end(&mut self, ticks: u32) { - self.inner.set_max_compare_value(ticks) + self.inner.set_max_compare_value(unwrap!(ticks.try_into())) } /// Reset the timer on each trigger @@ -327,7 +327,7 @@ pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { /// Get the end of the pulse in ticks from the trigger. pub fn pulse_end(&self) -> u32 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u32::MAX); max + 1 } @@ -339,13 +339,13 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { /// Get the start of the pulse in ticks from the trigger. pub fn pulse_delay(&mut self) -> u32 { - self.inner.get_compare_value(self.channel) + self.inner.get_compare_value(self.channel).into() } /// Set the start of the pulse in ticks from the trigger. pub fn set_pulse_delay(&mut self, delay: u32) { assert!(delay <= self.pulse_end()); - self.inner.set_compare_value(self.channel, delay); + self.inner.set_compare_value(self.channel, unwrap!(delay.try_into())); } /// Set the pulse width in ticks. diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 057ab011a..f2f00927d 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -91,16 +91,18 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Get the period tick count pub fn get_period_ticks(&self) -> u32 { - self.inner.get_capture_value(self.channel) + self.inner.get_capture_value(self.channel).into() } /// Get the pulse width tick count pub fn get_width_ticks(&self) -> u32 { - self.inner.get_capture_value(match self.channel { - Channel::Ch1 => Channel::Ch2, - Channel::Ch2 => Channel::Ch1, - _ => panic!("Invalid channel for PWM input"), - }) + self.inner + .get_capture_value(match self.channel { + Channel::Ch1 => Channel::Ch2, + Channel::Ch2 => Channel::Ch1, + _ => panic!("Invalid channel for PWM input"), + }) + .into() } /// Get the duty cycle in 100% diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index e8f97bf59..f5a4328d1 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -25,7 +25,7 @@ use crate::dma::ringbuffer::Error; /// ``` pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, - ring_buf: WritableRingBuffer<'d, u16>, + ring_buf: WritableRingBuffer<'d, T::Word>, channel: Channel, } @@ -33,7 +33,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { pub(crate) fn new( timer: ManuallyDrop>, channel: Channel, - ring_buf: WritableRingBuffer<'d, u16>, + ring_buf: WritableRingBuffer<'d, T::Word>, ) -> Self { Self { timer, @@ -55,18 +55,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { } /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. - pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + pub fn write_immediate(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { self.ring_buf.write_immediate(buf) } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + pub fn write(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { self.ring_buf.write(buf) } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, buffer: &[u16]) -> Result { + pub async fn write_exact(&mut self, buffer: &[T::Word]) -> Result { self.ring_buf.write_exact(buffer).await } @@ -140,7 +140,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.timer.get_max_compare_value(); + let max: u32 = self.timer.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 484e9fd81..8dc84cdfa 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -99,7 +99,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.timer.get_max_compare_value(); + let max: u32 = self.timer.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } @@ -174,7 +174,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { pub fn into_ring_buffered_channel( mut self, tx_dma: Peri<'d, impl super::UpDma>, - dma_buf: &'d mut [u16], + dma_buf: &'d mut [T::Word], ) -> RingBufferedPwmChannel<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -333,7 +333,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - let max = self.inner.get_max_compare_value(); + let max: u32 = self.inner.get_max_compare_value().into(); assert!(max < u16::MAX as u32); max as u16 + 1 } @@ -344,7 +344,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { self.inner.enable_channel(channel, true); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; @@ -385,7 +385,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[u16], + duty: &[T::Word], ) { self.inner.enable_update_dma(true); self.inner @@ -448,16 +448,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) + self.inner.get_compare_value(channel).into() } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() + 1 + self.inner.get_max_compare_value().into() + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { assert!(duty <= self.max_duty_cycle() as u32); - self.inner.set_compare_value(channel, duty) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } fn set_period

(&mut self, period: P) -- cgit From edb14f8c0966e1d22f396cbd631e5835a9a5104b Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 27 Nov 2025 20:19:06 -0600 Subject: stm32/timer: enable channels for waveform_up multi --- embassy-stm32/src/timer/complementary_pwm.rs | 5 +++++ embassy-stm32/src/timer/simple_pwm.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index d194899c6..996759060 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -263,6 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { ending_channel: Channel, duty: &[T::Word], ) { + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .filter(|ch| ch.index() >= starting_channel.index()) + .filter(|ch| ch.index() <= ending_channel.index()) + .for_each(|ch| self.inner.enable_channel(*ch, true)); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 8dc84cdfa..466c56db2 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -387,6 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ending_channel: Channel, duty: &[T::Word], ) { + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .filter(|ch| ch.index() >= starting_channel.index()) + .filter(|ch| ch.index() <= ending_channel.index()) + .for_each(|ch| self.inner.enable_channel(*ch, true)); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) -- cgit From b466ba29d2857815e5b25af7d8ab82d5b7e05e30 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 08:39:32 -0600 Subject: timer: allow 16 bit dma buffers for 32 bit timers. --- embassy-stm32/src/timer/complementary_pwm.rs | 18 ++++++++++++++---- embassy-stm32/src/timer/low_level.rs | 20 ++++++++++---------- embassy-stm32/src/timer/ringbuffered.rs | 27 ++++++++------------------- embassy-stm32/src/timer/simple_pwm.rs | 24 +++++++++++++++++------- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 996759060..b9434d37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -8,6 +8,7 @@ use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::PwmPin; use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; use crate::Peri; +use crate::dma::word::Word; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::timer::TimerChannel; @@ -220,8 +221,14 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// 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: Channel, duty: &[T::Word]) { + pub async fn waveform_up>( + &mut self, + dma: Peri<'_, impl super::UpDma>, + channel: Channel, + duty: &[W], + ) { self.inner.enable_channel(channel, true); + self.inner.set_compare_value(channel, 0.into()); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -256,18 +263,21 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub async fn waveform_up_multi_channel( + pub async fn waveform_up_multi_channel>( &mut self, dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[T::Word], + duty: &[W], ) { [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() .filter(|ch| ch.index() >= starting_channel.index()) .filter(|ch| ch.index() <= ending_channel.index()) - .for_each(|ch| self.inner.enable_channel(*ch, true)); + .for_each(|ch| { + self.inner.enable_channel(*ch, true); + self.inner.set_compare_value(*ch, 0.into()); + }); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 73a81bff1..1af66aec1 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -628,12 +628,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } /// Setup a ring buffer for the channel - pub fn setup_ring_buffer<'a>( + pub fn setup_ring_buffer<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - dma_buf: &'a mut [T::Word], - ) -> WritableRingBuffer<'a, T::Word> { + dma_buf: &'a mut [W], + ) -> WritableRingBuffer<'a, W> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -653,7 +653,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { WritableRingBuffer::new( dma, req, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut T::Word, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W, dma_buf, dma_transfer_option, ) @@ -664,11 +664,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub fn setup_update_dma<'a>( + pub fn setup_update_dma<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, channel: Channel, - duty: &'a [T::Word], + duty: &'a [W], ) -> Transfer<'a> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -690,7 +690,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().ccr(channel.index()).as_ptr() as *mut T::Word, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, dma_transfer_option, ) } @@ -725,12 +725,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub fn setup_update_dma_burst<'a>( + pub fn setup_update_dma_burst<'a, W: Word + Into>( &mut self, dma: Peri<'a, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &'a [T::Word], + duty: &'a [W], ) -> Transfer<'a> { let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; let start_ch_index = starting_channel.index(); @@ -766,7 +766,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_gp16().dmar().as_ptr() as *mut T::Word, + self.regs_gp16().dmar().as_ptr() as *mut W, dma_transfer_option, ) } diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index f5a4328d1..fbb6b19ea 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -7,6 +7,7 @@ use super::low_level::Timer; use super::{Channel, GeneralInstance4Channel}; use crate::dma::WritableRingBuffer; use crate::dma::ringbuffer::Error; +use crate::dma::word::Word; /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. /// @@ -23,17 +24,17 @@ use crate::dma::ringbuffer::Error; /// channel.start(); // Start DMA transfer /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles /// ``` -pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { +pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into> { timer: ManuallyDrop>, - ring_buf: WritableRingBuffer<'d, T::Word>, + ring_buf: WritableRingBuffer<'d, W>, channel: Channel, } -impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { +impl<'d, T: GeneralInstance4Channel, W: Word + Into> RingBufferedPwmChannel<'d, T, W> { pub(crate) fn new( timer: ManuallyDrop>, channel: Channel, - ring_buf: WritableRingBuffer<'d, T::Word>, + ring_buf: WritableRingBuffer<'d, W>, ) -> Self { Self { timer, @@ -55,18 +56,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { } /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. - pub fn write_immediate(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ring_buf.write_immediate(buf) } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn write(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ring_buf.write(buf) } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, buffer: &[T::Word]) -> Result { + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { self.ring_buf.write_exact(buffer).await } @@ -155,15 +156,3 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { self.timer.set_output_compare_mode(self.channel, mode); } } - -/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. -pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { - /// Channel 1 - pub ch1: RingBufferedPwmChannel<'d, T>, - /// Channel 2 - pub ch2: RingBufferedPwmChannel<'d, T>, - /// Channel 3 - pub ch3: RingBufferedPwmChannel<'d, T>, - /// Channel 4 - pub ch4: RingBufferedPwmChannel<'d, T>, -} diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 466c56db2..3f37ffcd9 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -7,6 +7,7 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; +use crate::dma::word::Word; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -171,11 +172,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// # Panics /// Panics if `dma_buf` is empty or longer than 65535 elements. - pub fn into_ring_buffered_channel( + pub fn into_ring_buffered_channel>( mut self, tx_dma: Peri<'d, impl super::UpDma>, - dma_buf: &'d mut [T::Word], - ) -> RingBufferedPwmChannel<'d, T> { + dma_buf: &'d mut [W], + ) -> RingBufferedPwmChannel<'d, T, W> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); self.timer.enable_update_dma(true); @@ -344,8 +345,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[T::Word]) { + pub async fn waveform_up>( + &mut self, + dma: Peri<'_, impl super::UpDma>, + channel: Channel, + duty: &[W], + ) { self.inner.enable_channel(channel, true); + self.inner.set_compare_value(channel, 0.into()); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -380,18 +387,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub async fn waveform_up_multi_channel( + pub async fn waveform_up_multi_channel>( &mut self, dma: Peri<'_, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[T::Word], + duty: &[W], ) { [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() .filter(|ch| ch.index() >= starting_channel.index()) .filter(|ch| ch.index() <= ending_channel.index()) - .for_each(|ch| self.inner.enable_channel(*ch, true)); + .for_each(|ch| { + self.inner.enable_channel(*ch, true); + self.inner.set_compare_value(*ch, 0.into()); + }); self.inner.enable_update_dma(true); self.inner .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) -- cgit From a1236b13c3d5fad687ae931a041ae463ac0612bb Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 09:03:10 -0600 Subject: timer: clamp compare value before dma --- embassy-stm32/src/dma/word.rs | 4 ++++ embassy-stm32/src/timer/complementary_pwm.rs | 4 ++-- embassy-stm32/src/timer/low_level.rs | 12 ++++++++++++ embassy-stm32/src/timer/simple_pwm.rs | 5 +++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs index fb1bde860..5c3bb8f7f 100644 --- a/embassy-stm32/src/dma/word.rs +++ b/embassy-stm32/src/dma/word.rs @@ -31,6 +31,10 @@ pub trait Word: SealedWord + Default + Copy + 'static { fn size() -> WordSize; /// Amount of bits of this word size. fn bits() -> usize; + /// Maximum value of this type. + fn max() -> usize { + (1 << Self::bits()) - 1 + } } macro_rules! impl_word { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index b9434d37b..4d04af685 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -228,7 +228,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { duty: &[W], ) { self.inner.enable_channel(channel, true); - self.inner.set_compare_value(channel, 0.into()); + self.inner.clamp_compare_value::(channel); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -276,7 +276,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { .filter(|ch| ch.index() <= ending_channel.index()) .for_each(|ch| { self.inner.enable_channel(*ch, true); - self.inner.set_compare_value(*ch, 0.into()); + self.inner.clamp_compare_value::(*ch); }); self.inner.enable_update_dma(true); self.inner diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 1af66aec1..6a70d2a40 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -627,6 +627,18 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); } + pub(crate) fn clamp_compare_value(&mut self, channel: Channel) { + self.set_compare_value( + channel, + unwrap!( + self.get_compare_value(channel) + .into() + .clamp(0, W::max() as u32) + .try_into() + ), + ); + } + /// Setup a ring buffer for the channel pub fn setup_ring_buffer<'a, W: Word + Into>( &mut self, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 3f37ffcd9..f4b84670f 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -179,6 +179,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { ) -> RingBufferedPwmChannel<'d, T, W> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + self.timer.clamp_compare_value::(self.channel); self.timer.enable_update_dma(true); RingBufferedPwmChannel::new( @@ -352,7 +353,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { duty: &[W], ) { self.inner.enable_channel(channel, true); - self.inner.set_compare_value(channel, 0.into()); + self.inner.clamp_compare_value::(channel); self.inner.enable_update_dma(true); self.inner.setup_update_dma(dma, channel, duty).await; self.inner.enable_update_dma(false); @@ -400,7 +401,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { .filter(|ch| ch.index() <= ending_channel.index()) .for_each(|ch| { self.inner.enable_channel(*ch, true); - self.inner.set_compare_value(*ch, 0.into()); + self.inner.clamp_compare_value::(*ch); }); self.inner.enable_update_dma(true); self.inner -- cgit From 970138277bdbf176a3a9320c9d0de9256945d21e Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 Dec 2025 09:44:07 -0600 Subject: timer: use u32 for high-level api --- embassy-stm32/src/timer/complementary_pwm.rs | 12 ++++++------ embassy-stm32/src/timer/simple_pwm.rs | 28 ++++++++++++--------------- examples/stm32f4/src/bin/pwm_complementary.rs | 2 +- examples/stm32f4/src/bin/ws2812_pwm.rs | 2 +- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 4d04af685..6ca13820a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -177,20 +177,20 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn get_max_duty(&self) -> u16 { + pub fn get_max_duty(&self) -> u32 { if self.inner.get_counting_mode().is_center_aligned() { - unwrap!(self.inner.get_max_compare_value().try_into()) + self.inner.get_max_compare_value().into() } else { - unwrap!(self.inner.get_max_compare_value().try_into()) + 1 + self.inner.get_max_compare_value().into() + 1 } } /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn set_duty(&mut self, channel: Channel, duty: u16) { + pub fn set_duty(&mut self, channel: Channel, duty: u32) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty.into()) + self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } /// Set the output polarity for a given channel. @@ -318,7 +318,7 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - assert!(duty <= self.get_max_duty()); + assert!(duty <= unwrap!(self.get_max_duty().try_into())); self.inner.set_compare_value(channel, unwrap!(duty.try_into())) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f4b84670f..b79ed364b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -99,18 +99,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn max_duty_cycle(&self) -> u16 { - let max: u32 = self.timer.get_max_compare_value().into(); - assert!(max < u16::MAX as u32); - max as u16 + 1 + pub fn max_duty_cycle(&self) -> u32 { + self.timer.get_max_compare_value().into() + 1 } /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. - pub fn set_duty_cycle(&mut self, duty: u16) { + pub fn set_duty_cycle(&mut self, duty: u32) { assert!(duty <= (*self).max_duty_cycle()); - self.timer.set_compare_value(self.channel, duty.into()) + self.timer.set_compare_value(self.channel, unwrap!(duty.try_into())) } /// Set the duty cycle to 0%, or always inactive. @@ -127,21 +125,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. - pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { + pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) { assert!(denom != 0); assert!(num <= denom); let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) #[allow(clippy::cast_possible_truncation)] - self.set_duty_cycle(duty as u16); + self.set_duty_cycle(unwrap!(duty.try_into())); } /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. pub fn set_duty_cycle_percent(&mut self, percent: u8) { - self.set_duty_cycle_fraction(u16::from(percent), 100) + self.set_duty_cycle_fraction(percent as u32, 100) } /// Get the duty for a given channel. @@ -334,10 +332,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn max_duty_cycle(&self) -> u16 { - let max: u32 = self.inner.get_max_compare_value().into(); - assert!(max < u16::MAX as u32); - max as u16 + 1 + pub fn max_duty_cycle(&self) -> u32 { + self.inner.get_max_compare_value().into() + 1 } /// Generate a sequence of PWM waveform @@ -417,11 +413,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { fn max_duty_cycle(&self) -> u16 { - self.max_duty_cycle() + unwrap!(self.max_duty_cycle().try_into()) } fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { - self.set_duty_cycle(duty); + self.set_duty_cycle(duty.into()); Ok(()) } @@ -436,7 +432,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl } fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - self.set_duty_cycle_fraction(num, denom); + self.set_duty_cycle_fraction(num.into(), denom.into()); Ok(()) } diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 50008a37b..5e39a06f5 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { ); let max = pwm.get_max_duty(); - pwm.set_dead_time(max / 1024); + pwm.set_dead_time((max / 1024) as u16); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index ccfd0661e..4e556f0d4 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) { // construct ws2812 non-return-to-zero (NRZ) code bit by bit // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low - let max_duty = ws2812_pwm.max_duty_cycle(); + let max_duty = ws2812_pwm.max_duty_cycle() as u16; let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing let n1 = 2 * n0; // ws2812 Bit 1 high level timing -- cgit