diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-10-20 16:39:30 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-20 16:39:30 +0000 |
| commit | b1d0947a18ffaa55d9307b2e563f7e3662486eb9 (patch) | |
| tree | c2b2920f63d83e32dd7f3f2ef18c64d73b997557 | |
| parent | 88ada521461031b7241b09e40aa56f4e64827967 (diff) | |
| parent | 5b3f75dc726afaf21b6c957b88f8eeb0c9ae322c (diff) | |
Merge pull request #1991 from diondokter/center-align
stm32: Add the ability to center-align timers
| -rw-r--r-- | embassy-stm32/src/timer/complementary_pwm.rs | 15 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 95 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 15 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/pwm.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/pwm_complementary.rs | 1 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/pwm.rs | 2 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/pwm.rs | 2 |
7 files changed, 115 insertions, 17 deletions
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> { | |||
| 57 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 57 | _ch4: Option<PwmPin<'d, T, Ch4>>, |
| 58 | _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>, | 58 | _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>, |
| 59 | freq: Hertz, | 59 | freq: Hertz, |
| 60 | counting_mode: CountingMode, | ||
| 60 | ) -> Self { | 61 | ) -> Self { |
| 61 | Self::new_inner(tim, freq) | 62 | Self::new_inner(tim, freq, counting_mode) |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self { | 65 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 65 | into_ref!(tim); | 66 | into_ref!(tim); |
| 66 | 67 | ||
| 67 | T::enable_and_reset(); | 68 | T::enable_and_reset(); |
| 68 | 69 | ||
| 69 | let mut this = Self { inner: tim }; | 70 | let mut this = Self { inner: tim }; |
| 70 | 71 | ||
| 71 | this.inner.set_frequency(freq); | 72 | this.inner.set_counting_mode(counting_mode); |
| 73 | this.set_freq(freq); | ||
| 72 | this.inner.start(); | 74 | this.inner.start(); |
| 73 | 75 | ||
| 74 | this.inner.enable_outputs(); | 76 | this.inner.enable_outputs(); |
| @@ -95,7 +97,12 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { | |||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | pub fn set_freq(&mut self, freq: Hertz) { | 99 | pub fn set_freq(&mut self, freq: Hertz) { |
| 98 | self.inner.set_frequency(freq); | 100 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 101 | 2u8 | ||
| 102 | } else { | ||
| 103 | 1u8 | ||
| 104 | }; | ||
| 105 | self.inner.set_frequency(freq * multiplier); | ||
| 99 | } | 106 | } |
| 100 | 107 | ||
| 101 | pub fn get_max_duty(&self) -> u16 { | 108 | 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 { | |||
| 29 | Self::regs().cr1().modify(|r| r.set_cen(false)); | 29 | Self::regs().cr1().modify(|r| r.set_cen(false)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | /// Reset the counter value to 0 | ||
| 32 | fn reset(&mut self) { | 33 | fn reset(&mut self) { |
| 33 | Self::regs().cnt().write(|r| r.set_cnt(0)); | 34 | Self::regs().cnt().write(|r| r.set_cnt(0)); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 37 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | ||
| 38 | /// | ||
| 39 | /// This means that in the default edge-aligned mode, | ||
| 40 | /// the timer counter will wrap around at the same frequency as is being set. | ||
| 41 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved | ||
| 42 | /// because it needs to count up and down. | ||
| 36 | fn set_frequency(&mut self, frequency: Hertz) { | 43 | fn set_frequency(&mut self, frequency: Hertz) { |
| 37 | let f = frequency.0; | 44 | let f = frequency.0; |
| 38 | let timer_f = Self::frequency().0; | 45 | let timer_f = Self::frequency().0; |
| @@ -85,8 +92,21 @@ pub(crate) mod sealed { | |||
| 85 | pub trait GeneralPurpose16bitInstance: Basic16bitInstance { | 92 | pub trait GeneralPurpose16bitInstance: Basic16bitInstance { |
| 86 | fn regs_gp16() -> crate::pac::timer::TimGp16; | 93 | fn regs_gp16() -> crate::pac::timer::TimGp16; |
| 87 | 94 | ||
| 88 | fn set_count_direction(&mut self, direction: vals::Dir) { | 95 | fn set_counting_mode(&mut self, mode: CountingMode) { |
| 89 | Self::regs_gp16().cr1().modify(|r| r.set_dir(direction)); | 96 | let (cms, dir) = mode.into(); |
| 97 | |||
| 98 | let timer_enabled = Self::regs().cr1().read().cen(); | ||
| 99 | // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. | ||
| 100 | // Changing direction is discouraged while the timer is running. | ||
| 101 | assert!(!timer_enabled); | ||
| 102 | |||
| 103 | Self::regs_gp16().cr1().modify(|r| r.set_dir(dir)); | ||
| 104 | Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) | ||
| 105 | } | ||
| 106 | |||
| 107 | fn get_counting_mode(&self) -> CountingMode { | ||
| 108 | let cr1 = Self::regs_gp16().cr1().read(); | ||
| 109 | (cr1.cms(), cr1.dir()).into() | ||
| 90 | } | 110 | } |
| 91 | 111 | ||
| 92 | fn set_clock_division(&mut self, ckd: vals::Ckd) { | 112 | fn set_clock_division(&mut self, ckd: vals::Ckd) { |
| @@ -293,6 +313,73 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs { | |||
| 293 | } | 313 | } |
| 294 | } | 314 | } |
| 295 | 315 | ||
| 316 | #[repr(u8)] | ||
| 317 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] | ||
| 318 | pub enum CountingMode { | ||
| 319 | #[default] | ||
| 320 | /// The timer counts up to the reload value and then resets back to 0. | ||
| 321 | EdgeAlignedUp, | ||
| 322 | /// The timer counts down to 0 and then resets back to the reload value. | ||
| 323 | EdgeAlignedDown, | ||
| 324 | /// The timer counts up to the reload value and then counts back to 0. | ||
| 325 | /// | ||
| 326 | /// The output compare interrupt flags of channels configured in output are | ||
| 327 | /// set when the counter is counting down. | ||
| 328 | CenterAlignedDownInterrupts, | ||
| 329 | /// The timer counts up to the reload value and then counts back to 0. | ||
| 330 | /// | ||
| 331 | /// The output compare interrupt flags of channels configured in output are | ||
| 332 | /// set when the counter is counting up. | ||
| 333 | CenterAlignedUpInterrupts, | ||
| 334 | /// The timer counts up to the reload value and then counts back to 0. | ||
| 335 | /// | ||
| 336 | /// The output compare interrupt flags of channels configured in output are | ||
| 337 | /// set when the counter is counting both up or down. | ||
| 338 | CenterAlignedBothInterrupts, | ||
| 339 | } | ||
| 340 | |||
| 341 | impl CountingMode { | ||
| 342 | pub fn is_edge_aligned(&self) -> bool { | ||
| 343 | match self { | ||
| 344 | CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, | ||
| 345 | _ => false, | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | pub fn is_center_aligned(&self) -> bool { | ||
| 350 | match self { | ||
| 351 | CountingMode::CenterAlignedDownInterrupts | ||
| 352 | | CountingMode::CenterAlignedUpInterrupts | ||
| 353 | | CountingMode::CenterAlignedBothInterrupts => true, | ||
| 354 | _ => false, | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | impl From<CountingMode> for (vals::Cms, vals::Dir) { | ||
| 360 | fn from(value: CountingMode) -> Self { | ||
| 361 | match value { | ||
| 362 | CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP), | ||
| 363 | CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN), | ||
| 364 | CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP), | ||
| 365 | CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP), | ||
| 366 | CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP), | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | impl From<(vals::Cms, vals::Dir)> for CountingMode { | ||
| 372 | fn from(value: (vals::Cms, vals::Dir)) -> Self { | ||
| 373 | match value { | ||
| 374 | (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, | ||
| 375 | (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, | ||
| 376 | (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, | ||
| 377 | (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, | ||
| 378 | (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, | ||
| 379 | } | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 296 | #[derive(Clone, Copy)] | 383 | #[derive(Clone, Copy)] |
| 297 | pub enum OutputCompareMode { | 384 | pub enum OutputCompareMode { |
| 298 | Frozen, | 385 | Frozen, |
| @@ -471,9 +558,5 @@ foreach_interrupt! { | |||
| 471 | crate::pac::$inst | 558 | crate::pac::$inst |
| 472 | } | 559 | } |
| 473 | } | 560 | } |
| 474 | |||
| 475 | |||
| 476 | |||
| 477 | |||
| 478 | }; | 561 | }; |
| 479 | } | 562 | } |
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> { | |||
| 56 | _ch3: Option<PwmPin<'d, T, Ch3>>, | 56 | _ch3: Option<PwmPin<'d, T, Ch3>>, |
| 57 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 57 | _ch4: Option<PwmPin<'d, T, Ch4>>, |
| 58 | freq: Hertz, | 58 | freq: Hertz, |
| 59 | counting_mode: CountingMode, | ||
| 59 | ) -> Self { | 60 | ) -> Self { |
| 60 | Self::new_inner(tim, freq) | 61 | Self::new_inner(tim, freq, counting_mode) |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self { | 64 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 64 | into_ref!(tim); | 65 | into_ref!(tim); |
| 65 | 66 | ||
| 66 | T::enable_and_reset(); | 67 | T::enable_and_reset(); |
| 67 | 68 | ||
| 68 | let mut this = Self { inner: tim }; | 69 | let mut this = Self { inner: tim }; |
| 69 | 70 | ||
| 70 | this.inner.set_frequency(freq); | 71 | this.inner.set_counting_mode(counting_mode); |
| 72 | this.set_freq(freq); | ||
| 71 | this.inner.start(); | 73 | this.inner.start(); |
| 72 | 74 | ||
| 73 | this.inner.enable_outputs(); | 75 | this.inner.enable_outputs(); |
| @@ -92,7 +94,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 92 | } | 94 | } |
| 93 | 95 | ||
| 94 | pub fn set_freq(&mut self, freq: Hertz) { | 96 | pub fn set_freq(&mut self, freq: Hertz) { |
| 95 | self.inner.set_frequency(freq); | 97 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 98 | 2u8 | ||
| 99 | } else { | ||
| 100 | 1u8 | ||
| 101 | }; | ||
| 102 | self.inner.set_frequency(freq * multiplier); | ||
| 96 | } | 103 | } |
| 97 | 104 | ||
| 98 | pub fn get_max_duty(&self) -> u16 { | 105 | 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) { | |||
| 17 | info!("Hello World!"); | 17 | info!("Hello World!"); |
| 18 | 18 | ||
| 19 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 19 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); |
| 20 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); | 20 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); |
| 21 | let max = pwm.get_max_duty(); | 21 | let max = pwm.get_max_duty(); |
| 22 | pwm.enable(Channel::Ch1); | 22 | pwm.enable(Channel::Ch1); |
| 23 | 23 | ||
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) { | |||
| 30 | None, | 30 | None, |
| 31 | None, | 31 | None, |
| 32 | khz(10), | 32 | khz(10), |
| 33 | Default::default(), | ||
| 33 | ); | 34 | ); |
| 34 | 35 | ||
| 35 | let max = pwm.get_max_duty(); | 36 | 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) { | |||
| 17 | info!("Hello World!"); | 17 | info!("Hello World!"); |
| 18 | 18 | ||
| 19 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); | 19 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); |
| 20 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); | 20 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); |
| 21 | let max = pwm.get_max_duty(); | 21 | let max = pwm.get_max_duty(); |
| 22 | pwm.enable(Channel::Ch1); | 22 | pwm.enable(Channel::Ch1); |
| 23 | 23 | ||
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) { | |||
| 39 | info!("Hello World!"); | 39 | info!("Hello World!"); |
| 40 | 40 | ||
| 41 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); | 41 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); |
| 42 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10)); | 42 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); |
| 43 | let max = pwm.get_max_duty(); | 43 | let max = pwm.get_max_duty(); |
| 44 | pwm.enable(Channel::Ch1); | 44 | pwm.enable(Channel::Ch1); |
| 45 | 45 | ||
