diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index e1baf6b2e..6654366cd 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -57,18 +57,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { _ch4: Option>, _ch4n: Option>, freq: Hertz, + counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq) + Self::new_inner(tim, freq, counting_mode) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { into_ref!(tim); T::enable_and_reset(); let mut this = Self { inner: tim }; - this.inner.set_frequency(freq); + this.inner.set_counting_mode(counting_mode); + this.set_freq(freq); this.inner.start(); this.inner.enable_outputs(); @@ -95,7 +97,12 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } pub fn set_freq(&mut self, freq: Hertz) { - self.inner.set_frequency(freq); + let multiplier = if self.inner.get_counting_mode().is_center_aligned() { + 2u8 + } else { + 1u8 + }; + self.inner.set_frequency(freq * multiplier); } pub fn get_max_duty(&self) -> u16 { diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 4b88834cb..913bfed2b 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -29,10 +29,17 @@ pub(crate) mod sealed { Self::regs().cr1().modify(|r| r.set_cen(false)); } + /// Reset the counter value to 0 fn reset(&mut self) { Self::regs().cnt().write(|r| r.set_cnt(0)); } + /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. + /// + /// This means that in the default edge-aligned mode, + /// the timer counter will wrap around at the same frequency as is being set. + /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved + /// because it needs to count up and down. fn set_frequency(&mut self, frequency: Hertz) { let f = frequency.0; let timer_f = Self::frequency().0; @@ -85,8 +92,21 @@ pub(crate) mod sealed { pub trait GeneralPurpose16bitInstance: Basic16bitInstance { fn regs_gp16() -> crate::pac::timer::TimGp16; - fn set_count_direction(&mut self, direction: vals::Dir) { - Self::regs_gp16().cr1().modify(|r| r.set_dir(direction)); + fn set_counting_mode(&mut self, mode: CountingMode) { + let (cms, dir) = mode.into(); + + let timer_enabled = Self::regs().cr1().read().cen(); + // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. + // Changing direction is discouraged while the timer is running. + assert!(!timer_enabled); + + Self::regs_gp16().cr1().modify(|r| r.set_dir(dir)); + Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) + } + + fn get_counting_mode(&self) -> CountingMode { + let cr1 = Self::regs_gp16().cr1().read(); + (cr1.cms(), cr1.dir()).into() } fn set_clock_division(&mut self, ckd: vals::Ckd) { @@ -293,6 +313,73 @@ impl From for stm32_metapac::timer::vals::CcmrInputCcs { } } +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum CountingMode { + #[default] + /// The timer counts up to the reload value and then resets back to 0. + EdgeAlignedUp, + /// The timer counts down to 0 and then resets back to the reload value. + EdgeAlignedDown, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting down. + CenterAlignedDownInterrupts, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting up. + CenterAlignedUpInterrupts, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting both up or down. + CenterAlignedBothInterrupts, +} + +impl CountingMode { + pub fn is_edge_aligned(&self) -> bool { + match self { + CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, + _ => false, + } + } + + pub fn is_center_aligned(&self) -> bool { + match self { + CountingMode::CenterAlignedDownInterrupts + | CountingMode::CenterAlignedUpInterrupts + | CountingMode::CenterAlignedBothInterrupts => true, + _ => false, + } + } +} + +impl From for (vals::Cms, vals::Dir) { + fn from(value: CountingMode) -> Self { + match value { + CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP), + CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN), + CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP), + CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP), + CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP), + } + } +} + +impl From<(vals::Cms, vals::Dir)> for CountingMode { + fn from(value: (vals::Cms, vals::Dir)) -> Self { + match value { + (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, + (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, + (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, + (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, + (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, + } + } +} + #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -471,9 +558,5 @@ foreach_interrupt! { crate::pac::$inst } } - - - - }; } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 01773ff3a..1cf0ad728 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -56,18 +56,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { _ch3: Option>, _ch4: Option>, freq: Hertz, + counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq) + Self::new_inner(tim, freq, counting_mode) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { into_ref!(tim); T::enable_and_reset(); let mut this = Self { inner: tim }; - this.inner.set_frequency(freq); + this.inner.set_counting_mode(counting_mode); + this.set_freq(freq); this.inner.start(); this.inner.enable_outputs(); @@ -92,7 +94,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } pub fn set_freq(&mut self, freq: Hertz) { - self.inner.set_frequency(freq); + let multiplier = if self.inner.get_counting_mode().is_center_aligned() { + 2u8 + } else { + 1u8 + }; + self.inner.set_frequency(freq * multiplier); } pub fn get_max_duty(&self) -> u16 { diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 538427e89..8e41d0e78 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index a8211f6e0..d925f26d9 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -30,6 +30,7 @@ async fn main(_spawner: Spawner) { None, None, khz(10), + Default::default(), ); let max = pwm.get_max_duty(); diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index eed0b6ad7..a84394005 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 3bf373c7e..973a10cdd 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10)); + let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1);