diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 33985a3f4..511eb6d37 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1017,6 +1017,7 @@ fn main() { (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), (("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)), (("lptim", "CH2"), quote!(crate::lptim::Channel1Pin)), + (("lptim", "OUT"), quote!(crate::lptim::OutputPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/lptim/channel.rs b/embassy-stm32/src/lptim/channel.rs new file mode 100644 index 000000000..17fc2fb86 --- /dev/null +++ b/embassy-stm32/src/lptim/channel.rs @@ -0,0 +1,18 @@ +/// Timer channel. +#[derive(Clone, Copy)] +pub enum Channel { + /// Channel 1. + Ch1, + /// Channel 2. + Ch2, +} + +impl Channel { + /// Get the channel index (0..1) + pub fn index(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, + } + } +} diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index dc5f1d5bb..9c10efa43 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -6,24 +6,12 @@ pub mod timer; use crate::rcc::RccPeripheral; /// Timer channel. -#[derive(Clone, Copy)] -pub enum Channel { - /// Channel 1. - Ch1, - /// Channel 2. - Ch2, -} - -impl Channel { - /// Get the channel index (0..1) - pub fn index(&self) -> usize { - match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - } - } -} +#[cfg(any(lptim_v2a, lptim_v2b))] +mod channel; +#[cfg(any(lptim_v2a, lptim_v2b))] +pub use channel::Channel; +pin_trait!(OutputPin, Instance); pin_trait!(Channel1Pin, Instance); pin_trait!(Channel2Pin, Instance); diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 24725f625..586148848 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs @@ -4,12 +4,18 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; -use super::timer::{ChannelDirection, Timer}; -use super::{Channel, Channel1Pin, Channel2Pin, Instance}; +use super::timer::Timer; +use super::Instance; +#[cfg(not(any(lptim_v2a, lptim_v2b)))] +use super::OutputPin; +#[cfg(any(lptim_v2a, lptim_v2b))] +use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin}; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::time::Hertz; use crate::Peripheral; +/// Output marker type. +pub enum Output {} /// Channel 1 marker type. pub enum Ch1 {} /// Channel 2 marker type. @@ -45,14 +51,44 @@ macro_rules! channel_impl { }; } +#[cfg(not(any(lptim_v2a, lptim_v2b)))] +channel_impl!(new, Output, OutputPin); +#[cfg(any(lptim_v2a, lptim_v2b))] channel_impl!(new_ch1, Ch1, Channel1Pin); +#[cfg(any(lptim_v2a, lptim_v2b))] channel_impl!(new_ch2, Ch2, Channel2Pin); /// PWM driver. pub struct Pwm<'d, T: Instance> { - inner: Timer<'d, T>, // _inner: PeripheralRef<'d, T>, + inner: Timer<'d, T>, } +#[cfg(not(any(lptim_v2a, lptim_v2b)))] +impl<'d, T: Instance> Pwm<'d, T> { + /// Create a new PWM driver. + pub fn new(tim: impl Peripheral

+ 'd, _output_pin: PwmPin<'d, T, Output>, freq: Hertz) -> Self { + Self::new_inner(tim, freq) + } + + /// Set the duty. + /// + /// 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, duty: u16) { + assert!(duty <= self.get_max_duty()); + self.inner.set_compare_value(duty) + } + + /// Get the duty. + /// + /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. + pub fn get_duty(&self) -> u16 { + self.inner.get_compare_value() + } + + fn post_init(&mut self) {} +} + +#[cfg(any(lptim_v2a, lptim_v2b))] impl<'d, T: Instance> Pwm<'d, T> { /// Create a new PWM driver. pub fn new( @@ -61,19 +97,7 @@ impl<'d, T: Instance> Pwm<'d, T> { _ch2_pin: Option>, freq: Hertz, ) -> Self { - let mut this = Self { inner: Timer::new(tim) }; - - this.inner.enable(); - this.set_frequency(freq); - - #[cfg(any(lptim_v2a, lptim_v2b))] - [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| { - this.inner.set_channel_direction(channel, ChannelDirection::OutputPwm); - }); - - this.inner.continuous_mode_start(); - - this + Self::new_inner(tim, freq) } /// Enable the given channel. @@ -91,21 +115,6 @@ impl<'d, T: Instance> Pwm<'d, T> { self.inner.get_channel_enable_state(channel) } - /// Set PWM frequency. - /// - /// Note: when you call this, the max duty value changes, so you will have to - /// call `set_duty` on all channels with the duty calculated based on the new max duty. - pub fn set_frequency(&mut self, frequency: Hertz) { - self.inner.set_frequency(frequency); - } - - /// 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 { - self.inner.get_max_compare_value() + 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. @@ -120,4 +129,40 @@ impl<'d, T: Instance> Pwm<'d, T> { pub fn get_duty(&self, channel: Channel) -> u16 { self.inner.get_compare_value(channel) } + + fn post_init(&mut self) { + [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| { + self.inner.set_channel_direction(channel, ChannelDirection::OutputPwm); + }); + } +} + +impl<'d, T: Instance> Pwm<'d, T> { + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + let mut this = Self { inner: Timer::new(tim) }; + + this.inner.enable(); + this.set_frequency(freq); + + this.post_init(); + + this.inner.continuous_mode_start(); + + this + } + + /// Set PWM frequency. + /// + /// Note: when you call this, the max duty value changes, so you will have to + /// call `set_duty` on all channels with the duty calculated based on the new max duty. + pub fn set_frequency(&mut self, frequency: Hertz) { + self.inner.set_frequency(frequency); + } + + /// 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 { + self.inner.get_max_compare_value() + 1 + } } diff --git a/embassy-stm32/src/lptim/timer/channel_direction.rs b/embassy-stm32/src/lptim/timer/channel_direction.rs new file mode 100644 index 000000000..a38df63cd --- /dev/null +++ b/embassy-stm32/src/lptim/timer/channel_direction.rs @@ -0,0 +1,18 @@ +use crate::pac::lptim::vals; + +/// Direction of a low-power timer channel +pub enum ChannelDirection { + /// Use channel as a PWM output + OutputPwm, + /// Use channel as an input capture + InputCapture, +} + +impl From for vals::Ccsel { + fn from(direction: ChannelDirection) -> Self { + match direction { + ChannelDirection::OutputPwm => vals::Ccsel::OUTPUTCOMPARE, + ChannelDirection::InputCapture => vals::Ccsel::INPUTCAPTURE, + } + } +} diff --git a/embassy-stm32/src/lptim/timer.rs b/embassy-stm32/src/lptim/timer/mod.rs similarity index 53% rename from embassy-stm32/src/lptim/timer.rs rename to embassy-stm32/src/lptim/timer/mod.rs index b354c1f61..e62fcab49 100644 --- a/embassy-stm32/src/lptim/timer.rs +++ b/embassy-stm32/src/lptim/timer/mod.rs @@ -1,118 +1,20 @@ //! Low-level timer driver. +mod prescaler; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; -use super::{Channel, Instance}; -use crate::pac::lptim::vals; +#[cfg(any(lptim_v2a, lptim_v2b))] +use super::channel::Channel; +#[cfg(any(lptim_v2a, lptim_v2b))] +mod channel_direction; +#[cfg(any(lptim_v2a, lptim_v2b))] +pub use channel_direction::ChannelDirection; +use prescaler::Prescaler; + +use super::Instance; use crate::rcc; use crate::time::Hertz; -/// Direction of a low-power timer channel -#[cfg(any(lptim_v2a, lptim_v2b))] -pub enum ChannelDirection { - /// Use channel as a PWM output - OutputPwm, - /// Use channel as an input capture - InputCapture, -} - -#[cfg(any(lptim_v2a, lptim_v2b))] -impl From for vals::Ccsel { - fn from(direction: ChannelDirection) -> Self { - match direction { - ChannelDirection::OutputPwm => vals::Ccsel::OUTPUTCOMPARE, - ChannelDirection::InputCapture => vals::Ccsel::INPUTCAPTURE, - } - } -} - -enum Prescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -impl From<&Prescaler> for vals::Presc { - fn from(prescaler: &Prescaler) -> Self { - match prescaler { - Prescaler::Div1 => vals::Presc::DIV1, - Prescaler::Div2 => vals::Presc::DIV2, - Prescaler::Div4 => vals::Presc::DIV4, - Prescaler::Div8 => vals::Presc::DIV8, - Prescaler::Div16 => vals::Presc::DIV16, - Prescaler::Div32 => vals::Presc::DIV32, - Prescaler::Div64 => vals::Presc::DIV64, - Prescaler::Div128 => vals::Presc::DIV128, - } - } -} - -impl From for Prescaler { - fn from(prescaler: vals::Presc) -> Self { - match prescaler { - vals::Presc::DIV1 => Prescaler::Div1, - vals::Presc::DIV2 => Prescaler::Div2, - vals::Presc::DIV4 => Prescaler::Div4, - vals::Presc::DIV8 => Prescaler::Div8, - vals::Presc::DIV16 => Prescaler::Div16, - vals::Presc::DIV32 => Prescaler::Div32, - vals::Presc::DIV64 => Prescaler::Div64, - vals::Presc::DIV128 => Prescaler::Div128, - } - } -} - -impl From<&Prescaler> for u32 { - fn from(prescaler: &Prescaler) -> Self { - match prescaler { - Prescaler::Div1 => 1, - Prescaler::Div2 => 2, - Prescaler::Div4 => 4, - Prescaler::Div8 => 8, - Prescaler::Div16 => 16, - Prescaler::Div32 => 32, - Prescaler::Div64 => 64, - Prescaler::Div128 => 128, - } - } -} - -impl From for Prescaler { - fn from(prescaler: u32) -> Self { - match prescaler { - 1 => Prescaler::Div1, - 2 => Prescaler::Div2, - 4 => Prescaler::Div4, - 8 => Prescaler::Div8, - 16 => Prescaler::Div16, - 32 => Prescaler::Div32, - 64 => Prescaler::Div64, - 128 => Prescaler::Div128, - _ => unreachable!(), - } - } -} - -impl Prescaler { - pub fn from_ticks(ticks: u32) -> Self { - // We need to scale down to a 16-bit range - (ticks >> 16).next_power_of_two().into() - } - - pub fn scale_down(&self, ticks: u32) -> u16 { - (ticks / u32::from(self)).try_into().unwrap() - } - - pub fn scale_up(&self, ticks: u16) -> u32 { - u32::from(self) * ticks as u32 - } -} - /// Low-level timer driver. pub struct Timer<'d, T: Instance> { _tim: PeripheralRef<'d, T>, @@ -148,14 +50,6 @@ impl<'d, T: Instance> Timer<'d, T> { T::regs().cr().modify(|w| w.set_cntstrt(true)); } - /// Set channel direction. - #[cfg(any(lptim_v2a, lptim_v2b))] - pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) { - T::regs() - .ccmr(0) - .modify(|w| w.set_ccsel(channel.index(), direction.into())); - } - /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. pub fn set_frequency(&self, frequency: Hertz) { let f = frequency.0; @@ -186,6 +80,14 @@ impl<'d, T: Instance> Timer<'d, T> { T::frequency() } + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. + pub fn get_max_compare_value(&self) -> u16 { + T::regs().arr().read().arr() + } +} + +#[cfg(any(lptim_v2a, lptim_v2b))] +impl<'d, T: Instance> Timer<'d, T> { /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { T::regs().ccmr(0).modify(|w| { @@ -208,8 +110,24 @@ impl<'d, T: Instance> Timer<'d, T> { T::regs().ccr(channel.index()).read().ccr() } - /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - pub fn get_max_compare_value(&self) -> u16 { - T::regs().arr().read().arr() + /// Set channel direction. + #[cfg(any(lptim_v2a, lptim_v2b))] + pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) { + T::regs() + .ccmr(0) + .modify(|w| w.set_ccsel(channel.index(), direction.into())); + } +} + +#[cfg(not(any(lptim_v2a, lptim_v2b)))] +impl<'d, T: Instance> Timer<'d, T> { + /// Set compare value for a channel. + pub fn set_compare_value(&self, value: u16) { + T::regs().cmp().modify(|w| w.set_cmp(value)); + } + + /// Get compare value for a channel. + pub fn get_compare_value(&self) -> u16 { + T::regs().cmp().read().cmp() } } diff --git a/embassy-stm32/src/lptim/timer/prescaler.rs b/embassy-stm32/src/lptim/timer/prescaler.rs new file mode 100644 index 000000000..5d2326faf --- /dev/null +++ b/embassy-stm32/src/lptim/timer/prescaler.rs @@ -0,0 +1,90 @@ +//! Low-level timer driver. + +use crate::pac::lptim::vals; + +pub enum Prescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl From<&Prescaler> for vals::Presc { + fn from(prescaler: &Prescaler) -> Self { + match prescaler { + Prescaler::Div1 => vals::Presc::DIV1, + Prescaler::Div2 => vals::Presc::DIV2, + Prescaler::Div4 => vals::Presc::DIV4, + Prescaler::Div8 => vals::Presc::DIV8, + Prescaler::Div16 => vals::Presc::DIV16, + Prescaler::Div32 => vals::Presc::DIV32, + Prescaler::Div64 => vals::Presc::DIV64, + Prescaler::Div128 => vals::Presc::DIV128, + } + } +} + +impl From for Prescaler { + fn from(prescaler: vals::Presc) -> Self { + match prescaler { + vals::Presc::DIV1 => Prescaler::Div1, + vals::Presc::DIV2 => Prescaler::Div2, + vals::Presc::DIV4 => Prescaler::Div4, + vals::Presc::DIV8 => Prescaler::Div8, + vals::Presc::DIV16 => Prescaler::Div16, + vals::Presc::DIV32 => Prescaler::Div32, + vals::Presc::DIV64 => Prescaler::Div64, + vals::Presc::DIV128 => Prescaler::Div128, + } + } +} + +impl From<&Prescaler> for u32 { + fn from(prescaler: &Prescaler) -> Self { + match prescaler { + Prescaler::Div1 => 1, + Prescaler::Div2 => 2, + Prescaler::Div4 => 4, + Prescaler::Div8 => 8, + Prescaler::Div16 => 16, + Prescaler::Div32 => 32, + Prescaler::Div64 => 64, + Prescaler::Div128 => 128, + } + } +} + +impl From for Prescaler { + fn from(prescaler: u32) -> Self { + match prescaler { + 1 => Prescaler::Div1, + 2 => Prescaler::Div2, + 4 => Prescaler::Div4, + 8 => Prescaler::Div8, + 16 => Prescaler::Div16, + 32 => Prescaler::Div32, + 64 => Prescaler::Div64, + 128 => Prescaler::Div128, + _ => unreachable!(), + } + } +} + +impl Prescaler { + pub fn from_ticks(ticks: u32) -> Self { + // We need to scale down to a 16-bit range + (ticks >> 16).next_power_of_two().into() + } + + pub fn scale_down(&self, ticks: u32) -> u16 { + (ticks / u32::from(self)).try_into().unwrap() + } + + pub fn scale_up(&self, ticks: u16) -> u32 { + u32::from(self) * ticks as u32 + } +}