Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stm32: Add the ability to center-align timers #1991

Merged
merged 6 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions embassy-stm32/src/timer/complementary_pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
_ch4: Option<PwmPin<'d, T, Ch4>>,
_ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Self::new_inner(tim, freq)
Self::new_inner(tim, freq, counting_mode)
}

fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
fn new_inner(tim: impl Peripheral<P = T> + '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();
Expand All @@ -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 {
Expand Down
95 changes: 89 additions & 6 deletions embassy-stm32/src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -293,6 +313,73 @@ impl From<InputTISelection> 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<CountingMode> 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,
Expand Down Expand Up @@ -471,9 +558,5 @@ foreach_interrupt! {
crate::pac::$inst
}
}




};
}
15 changes: 11 additions & 4 deletions embassy-stm32/src/timer/simple_pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
_ch3: Option<PwmPin<'d, T, Ch3>>,
_ch4: Option<PwmPin<'d, T, Ch4>>,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Self::new_inner(tim, freq)
Self::new_inner(tim, freq, counting_mode)
}

fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
fn new_inner(tim: impl Peripheral<P = T> + '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();
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion examples/stm32f4/src/bin/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions examples/stm32f4/src/bin/pwm_complementary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ async fn main(_spawner: Spawner) {
None,
None,
khz(10),
Default::default(),
);

let max = pwm.get_max_duty();
Expand Down
2 changes: 1 addition & 1 deletion examples/stm32g4/src/bin/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion examples/stm32h7/src/bin/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down