From 561126b0d6068d189477af18461bf1e467532516 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sun, 1 Oct 2023 23:09:01 +0200 Subject: [PATCH 1/5] stm32: Add the ability to center-align timers --- embassy-stm32/src/timer/complementary_pwm.rs | 6 ++- embassy-stm32/src/timer/mod.rs | 49 +++++++++++++++++++- embassy-stm32/src/timer/simple_pwm.rs | 6 ++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 4f033e3a25..bfe5137e73 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -57,11 +57,12 @@ 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(); @@ -70,6 +71,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { let mut this = Self { inner: tim }; this.inner.set_frequency(freq); + this.inner.set_counting_mode(counting_mode); this.inner.start(); this.inner.enable_outputs(true); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index d88fbcfdb5..52d1dc119a 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -75,8 +75,16 @@ 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.values(); + + 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 set_clock_division(&mut self, ckd: vals::Ckd) { @@ -265,6 +273,43 @@ impl From for stm32_metapac::timer::vals::CcmrInputCcs { } } +#[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 { + /// Get the register values to set the timer mode to the current variant + pub fn values(&self) -> (vals::Cms, vals::Dir) { + match self { + 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), + } + } +} + #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 9e28878b1f..cdf12189ae 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -56,11 +56,12 @@ 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(); @@ -69,6 +70,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { let mut this = Self { inner: tim }; this.inner.set_frequency(freq); + this.inner.set_counting_mode(counting_mode); this.inner.start(); this.inner.enable_outputs(true); From 05a9b113165dd1b124e7a0ac6462b6850bebedf2 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sun, 1 Oct 2023 23:39:53 +0200 Subject: [PATCH 2/5] Fix examples --- examples/stm32f4/src/bin/pwm.rs | 2 +- examples/stm32f4/src/bin/pwm_complementary.rs | 1 + examples/stm32g4/src/bin/pwm.rs | 2 +- examples/stm32h7/src/bin/pwm.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 1013a844e6..139b8de703 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 83a3c75379..dabbbf9acd 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 01e9cb476e..c62b11d135 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 5c8e57aa28..84e7df2676 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); From 137e47f98db224815231460766386d9a88b3d88a Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 2 Oct 2023 21:14:44 +0200 Subject: [PATCH 3/5] Do affect the frequency --- embassy-stm32/src/timer/complementary_pwm.rs | 9 ++++- embassy-stm32/src/timer/mod.rs | 41 ++++++++++++++++++-- embassy-stm32/src/timer/simple_pwm.rs | 9 ++++- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index bfe5137e73..72ff84b63c 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -70,8 +70,8 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { 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(true); @@ -98,7 +98,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 52d1dc119a..b8e6328807 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -76,7 +76,7 @@ pub(crate) mod sealed { fn regs_gp16() -> crate::pac::timer::TimGp16; fn set_counting_mode(&mut self, mode: CountingMode) { - let (cms, dir) = mode.values(); + 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. @@ -87,6 +87,11 @@ pub(crate) mod sealed { 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) { Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); } @@ -273,6 +278,7 @@ impl From for stm32_metapac::timer::vals::CcmrInputCcs { } } +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum CountingMode { #[default] @@ -298,9 +304,26 @@ pub enum CountingMode { } impl CountingMode { - /// Get the register values to set the timer mode to the current variant - pub fn values(&self) -> (vals::Cms, vals::Dir) { + 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), @@ -310,6 +333,18 @@ impl CountingMode { } } +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, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index cdf12189ae..b54f8cb075 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -69,8 +69,8 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { 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(true); @@ -95,7 +95,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 { From a9dc887060244403a1fe65611a1210f506e2858a Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 2 Oct 2023 21:41:30 +0200 Subject: [PATCH 4/5] Added clarifying comment --- embassy-stm32/src/timer/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b8e6328807..755c27806f 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; @@ -515,9 +522,5 @@ foreach_interrupt! { crate::pac::$inst } } - - - - }; } From 6f2995cd4c70a2b6c977f553a2d5efcd8216bba7 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 20 Oct 2023 10:41:39 +0200 Subject: [PATCH 5/5] Invert assert --- embassy-stm32/src/timer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 755c27806f..15eaf35368 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -88,7 +88,7 @@ pub(crate) mod sealed { 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); + assert!(!timer_enabled); Self::regs_gp16().cr1().modify(|r| r.set_dir(dir)); Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))