From a1c9a2e8bd83852d774255c82e71d2054c7c02e4 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 29 Aug 2024 16:18:43 -0400 Subject: [PATCH 1/9] First working draft of a lptim driver This driver is able to PWM a pin --- embassy-stm32/build.rs | 2 + embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/lptim/mod.rs | 145 ++++++++++++++++++++++++++++++ embassy-stm32/src/lptim/traits.rs | 95 ++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 embassy-stm32/src/lptim/mod.rs create mode 100644 embassy-stm32/src/lptim/traits.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 1984a1420..33985a3f4 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1015,6 +1015,8 @@ fn main() { (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), + (("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)), + (("lptim", "CH2"), quote!(crate::lptim::Channel1Pin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 98695e738..451f595e0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -89,6 +89,8 @@ pub mod i2s; pub mod ipcc; #[cfg(feature = "low-power")] pub mod low_power; +#[cfg(lptim)] +pub mod lptim; #[cfg(ltdc)] pub mod ltdc; #[cfg(opamp)] diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs new file mode 100644 index 000000000..3cf95149e --- /dev/null +++ b/embassy-stm32/src/lptim/mod.rs @@ -0,0 +1,145 @@ +//! Low-power timer (LPTIM) + +mod traits; + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use traits::Instance; + +use crate::gpio::{AfType, AnyPin, OutputType, Speed}; +// use crate::time::Hertz; +use crate::{rcc, Peripheral}; + +/// LPTIM master instance. +pub struct Master { + phantom: PhantomData, +} + +/// LPTIM channel 1 instance. +pub struct Ch1 { + phantom: PhantomData, +} + +/// LPTIM channel 2 instance. +pub struct Ch2 { + phantom: PhantomData, +} + +trait SealedChannel { + fn raw() -> usize; +} + +/// channel instance trait. +#[allow(private_bounds)] +pub trait Channel: SealedChannel {} + +/// LPTIM PWM pin. +pub struct PwmPin<'d, T, C> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(T, C)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident) => { + impl<'d, T: Instance> PwmPin<'d, T, $channel> { + #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + AfType::output(OutputType::PushPull, Speed::VeryHigh), + ); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl SealedChannel for $channel { + fn raw() -> usize { + $ch_num + } + } + impl Channel for $channel {} + }; +} + +channel_impl!(new_ch1, Ch1, 0, Channel1Pin); +channel_impl!(new_ch2, Ch2, 1, Channel2Pin); + +/// Struct used to divide a high resolution timer into multiple channels +pub struct Pwm<'d, T: Instance> { + _inner: PeripheralRef<'d, T>, + /// Master instance. + pub master: Master, + /// Channel 1. + pub ch_1: Ch1, + /// Channel 2. + pub ch_2: Ch2, +} + +impl<'d, T: Instance> Pwm<'d, T> { + /// Create a new LPTIM driver. + /// + /// This splits the LPTIM into its constituent parts, which you can then use individually. + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>>, + _ch2: Option>>, + ) -> Self { + Self::new_inner(tim) + } + + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + rcc::enable_and_reset::(); + + T::regs().cr().modify(|w| w.set_enable(true)); + + // Set frequency. Should be configurable. + // By default, 16MHz. We want 440Hz. + // That's 36363 cycles + T::regs().arr().write_value(stm32_metapac::lptim::regs::Arr(36363)); + + // Set duty cycle. Should be configurable. Should take care of channel too (only Ch1 now) + T::regs().ccr(0).write_value(stm32_metapac::lptim::regs::Ccr(18181)); + + // Enable channel as PWM. Default state anyway. Implement later. + // T::regs().ccmr().modify(|w| { + // w.set_ccsel(0, 0); + // w.set_ccsel(1, 0); + // }) + + // Enable output on pins. Should care about the channels! + T::regs().ccmr().modify(|w| { + w.set_cce(0, true); + w.set_cce(1, true); + }); + + Self { + _inner: tim, + master: Master { phantom: PhantomData }, + ch_1: Ch1 { phantom: PhantomData }, + ch_2: Ch2 { phantom: PhantomData }, + } + } + + /// Start + pub fn start(&mut self) { + T::regs().cr().modify(|w| w.set_cntstrt(true)); + } + + /// Stop + pub fn stop(&mut self) { + T::regs().cr().modify(|w| w.set_cntstrt(false)); + } +} + +pin_trait!(Channel1Pin, Instance); +pin_trait!(Channel2Pin, Instance); diff --git a/embassy-stm32/src/lptim/traits.rs b/embassy-stm32/src/lptim/traits.rs new file mode 100644 index 000000000..a8f890a78 --- /dev/null +++ b/embassy-stm32/src/lptim/traits.rs @@ -0,0 +1,95 @@ +use crate::rcc::RccPeripheral; +use crate::time::Hertz; + +#[repr(u8)] +#[derive(Clone, Copy)] +pub(crate) enum Prescaler { + Div1 = 1, + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, + Div32 = 32, + Div64 = 64, + Div128 = 128, +} + +impl From for u8 { + fn from(val: Prescaler) -> Self { + match val { + Prescaler::Div1 => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + Prescaler::Div128 => 0b111, + } + } +} + +impl From for Prescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => Prescaler::Div1, + 0b001 => Prescaler::Div2, + 0b010 => Prescaler::Div4, + 0b011 => Prescaler::Div8, + 0b100 => Prescaler::Div16, + 0b101 => Prescaler::Div32, + 0b110 => Prescaler::Div64, + 0b111 => Prescaler::Div128, + _ => unreachable!(), + } + } +} + +impl Prescaler { + pub fn compute_min_high_res(val: u32) -> Self { + *[ + Prescaler::Div1, + Prescaler::Div2, + Prescaler::Div4, + Prescaler::Div8, + Prescaler::Div16, + Prescaler::Div32, + Prescaler::Div64, + Prescaler::Div128, + ] + .iter() + .skip_while(|psc| **psc as u32 <= val) + .next() + .unwrap() + } + + pub fn compute_min_low_res(val: u32) -> Self { + *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] + .iter() + .skip_while(|psc| **psc as u32 <= val) + .next() + .unwrap() + } +} + +pub(crate) trait SealedInstance: RccPeripheral { + fn regs() -> crate::pac::lptim::LptimAdv; +} + +/// LPTIM instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static {} + +foreach_interrupt! { + ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => { + impl SealedInstance for crate::peripherals::$inst { + fn regs() -> crate::pac::lptim::LptimAdv { + crate::pac::$inst + } + } + + impl Instance for crate::peripherals::$inst { + + } + }; +} From 3b8859653e4028c194ceed9432a4f76c3c856816 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 4 Sep 2024 14:46:35 -0400 Subject: [PATCH 2/9] stm32: Clean up the lptim driver --- embassy-stm32/src/lptim/mod.rs | 166 +++++------------------ embassy-stm32/src/lptim/pwm.rs | 122 +++++++++++++++++ embassy-stm32/src/lptim/timer.rs | 212 ++++++++++++++++++++++++++++++ embassy-stm32/src/lptim/traits.rs | 95 ------------- 4 files changed, 369 insertions(+), 226 deletions(-) create mode 100644 embassy-stm32/src/lptim/pwm.rs create mode 100644 embassy-stm32/src/lptim/timer.rs delete mode 100644 embassy-stm32/src/lptim/traits.rs diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index 3cf95149e..baedf9fe2 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -1,145 +1,49 @@ //! Low-power timer (LPTIM) -mod traits; +pub mod pwm; +pub mod timer; -use core::marker::PhantomData; +use crate::rcc::RccPeripheral; -use embassy_hal_internal::{into_ref, PeripheralRef}; -pub use traits::Instance; - -use crate::gpio::{AfType, AnyPin, OutputType, Speed}; -// use crate::time::Hertz; -use crate::{rcc, Peripheral}; - -/// LPTIM master instance. -pub struct Master { - phantom: PhantomData, -} - -/// LPTIM channel 1 instance. -pub struct Ch1 { - phantom: PhantomData, -} - -/// LPTIM channel 2 instance. -pub struct Ch2 { - phantom: PhantomData, -} - -trait SealedChannel { - fn raw() -> usize; -} - -/// channel instance trait. -#[allow(private_bounds)] -pub trait Channel: SealedChannel {} - -/// LPTIM PWM pin. -pub struct PwmPin<'d, T, C> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(T, C)>, -} - -macro_rules! channel_impl { - ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident) => { - impl<'d, T: Instance> PwmPin<'d, T, $channel> { - #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output(OutputType::PushPull, Speed::VeryHigh), - ); - }); - PwmPin { - _pin: pin.map_into(), - phantom: PhantomData, - } - } - } - - impl SealedChannel for $channel { - fn raw() -> usize { - $ch_num - } - } - impl Channel for $channel {} - }; -} - -channel_impl!(new_ch1, Ch1, 0, Channel1Pin); -channel_impl!(new_ch2, Ch2, 1, Channel2Pin); - -/// Struct used to divide a high resolution timer into multiple channels -pub struct Pwm<'d, T: Instance> { - _inner: PeripheralRef<'d, T>, - /// Master instance. - pub master: Master, +/// Timer channel. +#[derive(Clone, Copy)] +pub enum Channel { /// Channel 1. - pub ch_1: Ch1, + Ch1, /// Channel 2. - pub ch_2: Ch2, + Ch2, } -impl<'d, T: Instance> Pwm<'d, T> { - /// Create a new LPTIM driver. - /// - /// This splits the LPTIM into its constituent parts, which you can then use individually. - pub fn new( - tim: impl Peripheral

+ 'd, - _ch1: Option>>, - _ch2: Option>>, - ) -> Self { - Self::new_inner(tim) - } - - fn new_inner(tim: impl Peripheral

+ 'd) -> Self { - into_ref!(tim); - - rcc::enable_and_reset::(); - - T::regs().cr().modify(|w| w.set_enable(true)); - - // Set frequency. Should be configurable. - // By default, 16MHz. We want 440Hz. - // That's 36363 cycles - T::regs().arr().write_value(stm32_metapac::lptim::regs::Arr(36363)); - - // Set duty cycle. Should be configurable. Should take care of channel too (only Ch1 now) - T::regs().ccr(0).write_value(stm32_metapac::lptim::regs::Ccr(18181)); - - // Enable channel as PWM. Default state anyway. Implement later. - // T::regs().ccmr().modify(|w| { - // w.set_ccsel(0, 0); - // w.set_ccsel(1, 0); - // }) - - // Enable output on pins. Should care about the channels! - T::regs().ccmr().modify(|w| { - w.set_cce(0, true); - w.set_cce(1, true); - }); - - Self { - _inner: tim, - master: Master { phantom: PhantomData }, - ch_1: Ch1 { phantom: PhantomData }, - ch_2: Ch2 { phantom: PhantomData }, +impl Channel { + /// Get the channel index (0..1) + pub fn index(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, } } - - /// Start - pub fn start(&mut self) { - T::regs().cr().modify(|w| w.set_cntstrt(true)); - } - - /// Stop - pub fn stop(&mut self) { - T::regs().cr().modify(|w| w.set_cntstrt(false)); - } } pin_trait!(Channel1Pin, Instance); pin_trait!(Channel2Pin, Instance); + +pub(crate) trait SealedInstance: RccPeripheral { + fn regs() -> crate::pac::lptim::LptimAdv; +} + +/// LPTIM instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static {} +foreach_interrupt! { + ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => { + impl SealedInstance for crate::peripherals::$inst { + fn regs() -> crate::pac::lptim::LptimAdv { + crate::pac::$inst + } + } + + impl Instance for crate::peripherals::$inst { + + } + }; +} diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs new file mode 100644 index 000000000..725aa676e --- /dev/null +++ b/embassy-stm32/src/lptim/pwm.rs @@ -0,0 +1,122 @@ +//! PWM driver. + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::timer::{ChannelDirection, Timer}; +use super::{Channel, Channel1Pin, Channel2Pin, Instance}; +use crate::gpio::{AfType, AnyPin, OutputType, Speed}; +use crate::time::Hertz; +use crate::Peripheral; + +/// Channel 1 marker type. +pub enum Ch1 {} +/// Channel 2 marker type. +pub enum Ch2 {} + +/// PWM pin wrapper. +/// +/// This wraps a pin to make it usable with PWM. +pub struct PwmPin<'d, T, C> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(T, C)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { + impl<'d, T: Instance> PwmPin<'d, T, $channel> { + #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + AfType::output(OutputType::PushPull, Speed::VeryHigh), + ); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +channel_impl!(new_ch1, Ch1, Channel1Pin); +channel_impl!(new_ch2, Ch2, Channel2Pin); + +/// PWM driver. +pub struct Pwm<'d, T: Instance> { + inner: Timer<'d, T>, // _inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Pwm<'d, T> { + /// Create a new PWM driver. + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1_pin: Option>, + _ch2_pin: Option>, + freq: Hertz, + ) -> Self { + let mut this = Self { inner: Timer::new(tim) }; + + this.inner.enable(); + this.set_frequency(freq); + + [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| { + this.inner.set_channel_direction(channel, ChannelDirection::OutputPwm); + }); + + this.inner.continuous_mode_start(); + + this + } + + /// Enable the given channel. + pub fn enable(&mut self, channel: Channel) { + self.inner.enable_channel(channel, true); + } + + /// Disable the given channel. + pub fn disable(&mut self, channel: Channel) { + self.inner.enable_channel(channel, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self, channel: Channel) -> bool { + 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. + pub fn set_duty(&mut self, channel: Channel, duty: u16) { + assert!(duty <= self.get_max_duty()); + self.inner.set_compare_value(channel, duty) + } + + /// Get 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. + pub fn get_duty(&self, channel: Channel) -> u16 { + self.inner.get_compare_value(channel) + } +} diff --git a/embassy-stm32/src/lptim/timer.rs b/embassy-stm32/src/lptim/timer.rs new file mode 100644 index 000000000..06392b2e7 --- /dev/null +++ b/embassy-stm32/src/lptim/timer.rs @@ -0,0 +1,212 @@ +//! Low-level timer driver. + +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; + +use super::{Channel, Instance}; +use crate::pac::lptim::vals; +use crate::rcc; +use crate::time::Hertz; + +/// 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, + } + } +} + +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>, +} + +impl<'d, T: Instance> Timer<'d, T> { + /// Create a new timer driver. + pub fn new(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + rcc::enable_and_reset::(); + + Self { _tim: tim } + } + + /// Enable the timer. + pub fn enable(&self) { + T::regs().cr().modify(|w| w.set_enable(true)); + } + + /// Disable the timer. + pub fn disable(&self) { + T::regs().cr().modify(|w| w.set_enable(false)); + } + + /// Start the timer in single pulse mode. + pub fn single_mode_start(&self) { + T::regs().cr().modify(|w| w.set_sngstrt(true)); + } + + /// Start the timer in continuous mode. + pub fn continuous_mode_start(&self) { + T::regs().cr().modify(|w| w.set_cntstrt(true)); + } + + /// Set channel direction. + pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) { + T::regs() + .ccmr() + .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; + assert!(f > 0); + + let pclk_f = T::frequency().0; + + let pclk_ticks_per_timer_period = pclk_f / f; + + let psc = Prescaler::from_ticks(pclk_ticks_per_timer_period); + let arr = psc.scale_down(pclk_ticks_per_timer_period); + + T::regs().cfgr().modify(|r| r.set_presc((&psc).into())); + T::regs().arr().modify(|r| r.set_arr(arr.into())); + } + + /// Get the timer frequency. + pub fn get_frequency(&self) -> Hertz { + let pclk_f = T::frequency(); + let arr = T::regs().arr().read().arr(); + let psc = Prescaler::from(T::regs().cfgr().read().presc()); + + pclk_f / psc.scale_up(arr) + } + + /// Get the clock frequency of the timer (before prescaler is applied). + pub fn get_clock_frequency(&self) -> Hertz { + T::frequency() + } + + /// Enable/disable a channel. + pub fn enable_channel(&self, channel: Channel, enable: bool) { + T::regs().ccmr().modify(|w| { + w.set_cce(channel.index(), enable); + }); + } + + /// Get enable/disable state of a channel + pub fn get_channel_enable_state(&self, channel: Channel) -> bool { + T::regs().ccmr().read().cce(channel.index()) + } + + /// Set compare value for a channel. + pub fn set_compare_value(&self, channel: Channel, value: u16) { + T::regs().ccr(channel.index()).modify(|w| w.set_ccr(value)); + } + + /// Get compare value for a channel. + pub fn get_compare_value(&self, channel: Channel) -> u16 { + 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() + } +} diff --git a/embassy-stm32/src/lptim/traits.rs b/embassy-stm32/src/lptim/traits.rs deleted file mode 100644 index a8f890a78..000000000 --- a/embassy-stm32/src/lptim/traits.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::rcc::RccPeripheral; -use crate::time::Hertz; - -#[repr(u8)] -#[derive(Clone, Copy)] -pub(crate) enum Prescaler { - Div1 = 1, - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, - Div32 = 32, - Div64 = 64, - Div128 = 128, -} - -impl From for u8 { - fn from(val: Prescaler) -> Self { - match val { - Prescaler::Div1 => 0b000, - Prescaler::Div2 => 0b001, - Prescaler::Div4 => 0b010, - Prescaler::Div8 => 0b011, - Prescaler::Div16 => 0b100, - Prescaler::Div32 => 0b101, - Prescaler::Div64 => 0b110, - Prescaler::Div128 => 0b111, - } - } -} - -impl From for Prescaler { - fn from(val: u8) -> Self { - match val { - 0b000 => Prescaler::Div1, - 0b001 => Prescaler::Div2, - 0b010 => Prescaler::Div4, - 0b011 => Prescaler::Div8, - 0b100 => Prescaler::Div16, - 0b101 => Prescaler::Div32, - 0b110 => Prescaler::Div64, - 0b111 => Prescaler::Div128, - _ => unreachable!(), - } - } -} - -impl Prescaler { - pub fn compute_min_high_res(val: u32) -> Self { - *[ - Prescaler::Div1, - Prescaler::Div2, - Prescaler::Div4, - Prescaler::Div8, - Prescaler::Div16, - Prescaler::Div32, - Prescaler::Div64, - Prescaler::Div128, - ] - .iter() - .skip_while(|psc| **psc as u32 <= val) - .next() - .unwrap() - } - - pub fn compute_min_low_res(val: u32) -> Self { - *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] - .iter() - .skip_while(|psc| **psc as u32 <= val) - .next() - .unwrap() - } -} - -pub(crate) trait SealedInstance: RccPeripheral { - fn regs() -> crate::pac::lptim::LptimAdv; -} - -/// LPTIM instance trait. -#[allow(private_bounds)] -pub trait Instance: SealedInstance + 'static {} - -foreach_interrupt! { - ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => { - impl SealedInstance for crate::peripherals::$inst { - fn regs() -> crate::pac::lptim::LptimAdv { - crate::pac::$inst - } - } - - impl Instance for crate::peripherals::$inst { - - } - }; -} From b449e04a88d76b41aae91d98bd66bf2754637d03 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 4 Sep 2024 18:06:46 -0400 Subject: [PATCH 3/9] stm32: Temporary fix around incomplete stm32-metapac --- embassy-stm32/src/lptim/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index baedf9fe2..327a1d44b 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -35,7 +35,7 @@ pub(crate) trait SealedInstance: RccPeripheral { #[allow(private_bounds)] pub trait Instance: SealedInstance + 'static {} foreach_interrupt! { - ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => { + ($inst:ident, lptim, LPTIM_ADV, GLOBAL, $irq:ident) => { impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::lptim::LptimAdv { crate::pac::$inst From 37130f45e4f74f7b4faeb7cac017c4588e5b422a Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 5 Sep 2024 11:39:09 -0400 Subject: [PATCH 4/9] stm32: Use the updated LPTIM pac --- embassy-stm32/src/lptim/mod.rs | 6 +++--- embassy-stm32/src/lptim/pwm.rs | 1 + embassy-stm32/src/lptim/timer.rs | 9 ++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index 327a1d44b..dc5f1d5bb 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -28,16 +28,16 @@ pin_trait!(Channel1Pin, Instance); pin_trait!(Channel2Pin, Instance); pub(crate) trait SealedInstance: RccPeripheral { - fn regs() -> crate::pac::lptim::LptimAdv; + fn regs() -> crate::pac::lptim::Lptim; } /// LPTIM instance trait. #[allow(private_bounds)] pub trait Instance: SealedInstance + 'static {} foreach_interrupt! { - ($inst:ident, lptim, LPTIM_ADV, GLOBAL, $irq:ident) => { + ($inst:ident, lptim, LPTIM, UP, $irq:ident) => { impl SealedInstance for crate::peripherals::$inst { - fn regs() -> crate::pac::lptim::LptimAdv { + fn regs() -> crate::pac::lptim::Lptim { crate::pac::$inst } } diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 725aa676e..24725f625 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs @@ -66,6 +66,7 @@ impl<'d, T: Instance> Pwm<'d, T> { 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); }); diff --git a/embassy-stm32/src/lptim/timer.rs b/embassy-stm32/src/lptim/timer.rs index 06392b2e7..b354c1f61 100644 --- a/embassy-stm32/src/lptim/timer.rs +++ b/embassy-stm32/src/lptim/timer.rs @@ -8,6 +8,7 @@ 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, @@ -15,6 +16,7 @@ pub enum ChannelDirection { InputCapture, } +#[cfg(any(lptim_v2a, lptim_v2b))] impl From for vals::Ccsel { fn from(direction: ChannelDirection) -> Self { match direction { @@ -147,9 +149,10 @@ impl<'d, T: Instance> Timer<'d, T> { } /// Set channel direction. + #[cfg(any(lptim_v2a, lptim_v2b))] pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) { T::regs() - .ccmr() + .ccmr(0) .modify(|w| w.set_ccsel(channel.index(), direction.into())); } @@ -185,14 +188,14 @@ impl<'d, T: Instance> Timer<'d, T> { /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { - T::regs().ccmr().modify(|w| { + T::regs().ccmr(0).modify(|w| { w.set_cce(channel.index(), enable); }); } /// Get enable/disable state of a channel pub fn get_channel_enable_state(&self, channel: Channel) -> bool { - T::regs().ccmr().read().cce(channel.index()) + T::regs().ccmr(0).read().cce(channel.index()) } /// Set compare value for a channel. From 652133bce48f39ea5f379d056ca46a7f2ca3a99a Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 5 Sep 2024 15:10:39 -0400 Subject: [PATCH 5/9] stm32: Support LPTIM v1 and v2 --- embassy-stm32/build.rs | 1 + embassy-stm32/src/lptim/channel.rs | 18 ++ embassy-stm32/src/lptim/mod.rs | 22 +-- embassy-stm32/src/lptim/pwm.rs | 107 ++++++++---- .../src/lptim/timer/channel_direction.rs | 18 ++ .../src/lptim/{timer.rs => timer/mod.rs} | 156 +++++------------- embassy-stm32/src/lptim/timer/prescaler.rs | 90 ++++++++++ 7 files changed, 245 insertions(+), 167 deletions(-) create mode 100644 embassy-stm32/src/lptim/channel.rs create mode 100644 embassy-stm32/src/lptim/timer/channel_direction.rs rename embassy-stm32/src/lptim/{timer.rs => timer/mod.rs} (53%) create mode 100644 embassy-stm32/src/lptim/timer/prescaler.rs 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 + } +} From ac319970b82e3e613c35d45a11f31423204115e0 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 5 Sep 2024 16:30:45 -0400 Subject: [PATCH 6/9] stm32: Work around LPTIM4 --- embassy-stm32/src/lptim/mod.rs | 25 ++++++++++++++++++------- embassy-stm32/src/lptim/pwm.rs | 4 ++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index 9c10efa43..05ed4de44 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -11,17 +11,23 @@ mod channel; #[cfg(any(lptim_v2a, lptim_v2b))] pub use channel::Channel; -pin_trait!(OutputPin, Instance); -pin_trait!(Channel1Pin, Instance); -pin_trait!(Channel2Pin, Instance); +pin_trait!(OutputPin, BasicInstance); +pin_trait!(Channel1Pin, BasicInstance); +pin_trait!(Channel2Pin, BasicInstance); pub(crate) trait SealedInstance: RccPeripheral { fn regs() -> crate::pac::lptim::Lptim; } +pub(crate) trait SealedBasicInstance: RccPeripheral {} + +/// LPTIM basic instance trait. +#[allow(private_bounds)] +pub trait BasicInstance: SealedBasicInstance + 'static {} /// LPTIM instance trait. #[allow(private_bounds)] -pub trait Instance: SealedInstance + 'static {} +pub trait Instance: BasicInstance + SealedInstance + 'static {} + foreach_interrupt! { ($inst:ident, lptim, LPTIM, UP, $irq:ident) => { impl SealedInstance for crate::peripherals::$inst { @@ -29,9 +35,14 @@ foreach_interrupt! { crate::pac::$inst } } - - impl Instance for crate::peripherals::$inst { - + impl SealedBasicInstance for crate::peripherals::$inst { } + impl BasicInstance for crate::peripherals::$inst {} + impl Instance for crate::peripherals::$inst {} + }; + ($inst:ident, lptim, LPTIM_BASIC, UP, $irq:ident) => { + impl SealedBasicInstance for crate::peripherals::$inst { + } + impl BasicInstance for crate::peripherals::$inst {} }; } diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 586148848..1f43eb6ee 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs @@ -5,11 +5,11 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; 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 super::{BasicInstance, Instance}; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::time::Hertz; use crate::Peripheral; @@ -31,7 +31,7 @@ pub struct PwmPin<'d, T, C> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: Instance> PwmPin<'d, T, $channel> { + impl<'d, T: BasicInstance> PwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); From 69208daf6dba8ef5285e309219991dd63c38506e Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 5 Sep 2024 17:42:09 -0400 Subject: [PATCH 7/9] stm32: Fixing stupid typo (thanks @dirbaio) --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 511eb6d37..19cf193d9 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1016,7 +1016,7 @@ fn main() { (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), (("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)), - (("lptim", "CH2"), quote!(crate::lptim::Channel1Pin)), + (("lptim", "CH2"), quote!(crate::lptim::Channel2Pin)), (("lptim", "OUT"), quote!(crate::lptim::OutputPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), From d69f68b8c006dc785652a74fbe730583900d1923 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 6 Sep 2024 20:15:45 -0400 Subject: [PATCH 8/9] stm32: Use a GLOBAL interrupt for lptim --- embassy-stm32/src/lptim/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs index 05ed4de44..1649cc5b4 100644 --- a/embassy-stm32/src/lptim/mod.rs +++ b/embassy-stm32/src/lptim/mod.rs @@ -29,7 +29,7 @@ pub trait BasicInstance: SealedBasicInstance + 'static {} pub trait Instance: BasicInstance + SealedInstance + 'static {} foreach_interrupt! { - ($inst:ident, lptim, LPTIM, UP, $irq:ident) => { + ($inst:ident, lptim, LPTIM, GLOBAL, $irq:ident) => { impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::lptim::Lptim { crate::pac::$inst @@ -40,7 +40,7 @@ foreach_interrupt! { impl BasicInstance for crate::peripherals::$inst {} impl Instance for crate::peripherals::$inst {} }; - ($inst:ident, lptim, LPTIM_BASIC, UP, $irq:ident) => { + ($inst:ident, lptim, LPTIM_BASIC, GLOBAL, $irq:ident) => { impl SealedBasicInstance for crate::peripherals::$inst { } impl BasicInstance for crate::peripherals::$inst {} From c739091085eafeb01703626284428905d75aa98f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Sep 2024 01:00:23 +0200 Subject: [PATCH 9/9] Update stm32-metapac. --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/adc/mod.rs | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9a6a5908e..3c6484c96 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5ef354f3e49f790e47f5c818f243459742c9b83b" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" } vcell = "0.1.3" nb = "1.0.0" @@ -99,7 +99,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5ef354f3e49f790e47f5c818f243459742c9b83b", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 2ac0c0083..4ab82c1d9 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -4,7 +4,7 @@ #![allow(missing_docs)] // TODO #![cfg_attr(adc_f3_v2, allow(unused))] -#[cfg(not(adc_f3_v2))] +#[cfg(not(any(adc_f3_v2, adc_u5)))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_f3, path = "f3.rs")] #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] @@ -19,14 +19,16 @@ mod _version; use core::marker::PhantomData; #[allow(unused)] -#[cfg(not(adc_f3_v2))] +#[cfg(not(any(adc_f3_v2, adc_u5)))] pub use _version::*; #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] use embassy_sync::waitqueue::AtomicWaker; +#[cfg(not(any(adc_u5)))] pub use crate::pac::adc::vals; -#[cfg(not(any(adc_f1, adc_f3_v2)))] +#[cfg(not(any(adc_f1, adc_f3_v2, adc_u5)))] pub use crate::pac::adc::vals::Res as Resolution; +#[cfg(not(any(adc_u5)))] pub use crate::pac::adc::vals::SampleTime; use crate::peripherals; @@ -36,7 +38,7 @@ dma_trait!(RxDma, Instance); pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] + #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_u5)))] sample_time: SampleTime, } @@ -57,7 +59,7 @@ impl State { trait SealedInstance { #[allow(unused)] fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5)))] #[allow(unused)] fn common_regs() -> crate::pac::adccommon::AdcCommon; #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] @@ -163,7 +165,7 @@ foreach_adc!( crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { return crate::pac::$common_inst } @@ -200,7 +202,7 @@ macro_rules! impl_adc_pin { /// Get the maximum reading value for this resolution. /// /// This is `2**n - 1`. -#[cfg(not(any(adc_f1, adc_f3_v2)))] +#[cfg(not(any(adc_f1, adc_f3_v2, adc_u5)))] pub const fn resolution_to_max_count(res: Resolution) -> u32 { match res { #[cfg(adc_v4)]