From 91552625565e78a0b1680803676523c211eeda75 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 4 Aug 2024 15:52:27 +0300 Subject: [PATCH] move pins after --- CHANGELOG.md | 2 + examples/pwm-dead-time.rs | 25 +- examples/pwm-input.rs | 12 +- examples/pwm-sinus.rs | 30 +- examples/pwm.rs | 13 +- src/timer.rs | 71 ++- src/timer/hal_02.rs | 88 +--- src/timer/hal_1.rs | 24 +- src/timer/pwm.rs | 931 ++++++++++++++------------------------ 9 files changed, 463 insertions(+), 733 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42116539..46518fe0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - RTC: `ClockSource` enum instead of generic [#785] - Bump MSRV to 1.62 [#778] + - Move PWM pins connecting after PWM inialization [#791] - Use `stm32f4-staging` until `stm32f4` is released [#706] - use GPIO pac fields instead of raw write [#777] - RTIC2 monotonics fix: CC1 instead of CC3 [#771] @@ -51,6 +52,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#778]: https://github.com/stm32-rs/stm32f4xx-hal/pull/778 [#783]: https://github.com/stm32-rs/stm32f4xx-hal/pull/783 [#785]: https://github.com/stm32-rs/stm32f4xx-hal/pull/785 +[#791]: https://github.com/stm32-rs/stm32f4xx-hal/pull/791 [#796]: https://github.com/stm32-rs/stm32f4xx-hal/pull/796 ## [v0.21.0] - 2024-05-30 diff --git a/examples/pwm-dead-time.rs b/examples/pwm-dead-time.rs index 390ac5eb..071481ef 100644 --- a/examples/pwm-dead-time.rs +++ b/examples/pwm-dead-time.rs @@ -9,12 +9,7 @@ use panic_halt as _; // panic handler use cortex_m_rt::entry; use stm32f4xx_hal as hal; -use hal::{ - pac, - prelude::*, - timer::Channel, - timer::{Channel1, Polarity}, -}; +use hal::{pac, prelude::*, timer::Polarity}; #[entry] fn main() -> ! { @@ -25,21 +20,21 @@ fn main() -> ! { let gpioa = dp.GPIOA.split(); - let channels = Channel1::new(gpioa.pa8).with_complementary(gpioa.pa7); + let (mut pwm_mngr, (pwm_c1, ..)) = dp.TIM1.pwm_hz(20.kHz(), &clocks); - let mut pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &clocks); + let mut pwm_c1 = pwm_c1.with(gpioa.pa8).with_complementary(gpioa.pa7); - let max_duty: u16 = pwm.get_max_duty(); + let max_duty: u16 = pwm_c1.get_max_duty(); - pwm.set_polarity(Channel::C1, Polarity::ActiveHigh); - pwm.set_complementary_polarity(Channel::C1, Polarity::ActiveHigh); + pwm_c1.set_polarity(Polarity::ActiveHigh); + pwm_c1.set_complementary_polarity(Polarity::ActiveHigh); - pwm.set_duty(Channel::C1, max_duty / 2); + pwm_c1.set_duty(max_duty / 2); - pwm.set_dead_time(200); + pwm_mngr.set_dead_time(200); - pwm.enable(Channel::C1); - pwm.enable_complementary(Channel::C1); + pwm_c1.enable(); + pwm_c1.enable_complementary(); } loop { diff --git a/examples/pwm-input.rs b/examples/pwm-input.rs index d96b318d..e2189eac 100644 --- a/examples/pwm-input.rs +++ b/examples/pwm-input.rs @@ -6,11 +6,7 @@ use panic_halt as _; use cortex_m_rt::entry; -use stm32f4xx_hal::{ - pac, - prelude::*, - timer::{Channel1, Channel2, Timer}, -}; +use stm32f4xx_hal::{pac, prelude::*, timer::Timer}; #[entry] fn main() -> ! { @@ -22,10 +18,10 @@ fn main() -> ! { let gpioa = dp.GPIOA.split(); let gpioc = dp.GPIOC.split(); - let channels = (Channel1::new(gpioa.pa8), Channel2::new(gpioa.pa9)); // configure tim1 as a PWM output of known frequency. - let pwm = Timer::new(dp.TIM1, &clocks).pwm_hz(channels, 501.Hz()); - let (mut ch1, _ch2) = pwm.split(); + let (_, (ch1, ch2, ..)) = Timer::new(dp.TIM1, &clocks).pwm_hz(501.Hz()); + let mut ch1 = ch1.with(gpioa.pa8); + let mut _ch2 = ch2.with(gpioa.pa9); let max_duty = ch1.get_max_duty(); ch1.set_duty(max_duty / 2); ch1.enable(); diff --git a/examples/pwm-sinus.rs b/examples/pwm-sinus.rs index d9b34705..3121d365 100644 --- a/examples/pwm-sinus.rs +++ b/examples/pwm-sinus.rs @@ -8,11 +8,7 @@ use panic_halt as _; use core::f32::consts::FRAC_PI_2; use cortex_m_rt::entry; use micromath::F32Ext; -use stm32f4xx_hal::{ - pac, - prelude::*, - timer::{Channel, Channel1, Channel2}, -}; +use stm32f4xx_hal::{pac, prelude::*}; #[entry] fn main() -> ! { @@ -22,11 +18,13 @@ fn main() -> ! { let clocks = rcc.cfgr.use_hse(25.MHz()).freeze(); let gpioa = dp.GPIOA.split(); - let channels = (Channel1::new(gpioa.pa8), Channel2::new(gpioa.pa9)); - let mut pwm = dp.TIM1.pwm_us(channels, 100.micros(), &clocks); + let (_, (pwm_c1, pwm_c2, ..)) = dp.TIM1.pwm_us(100.micros(), &clocks); + let mut pwm_c1 = pwm_c1.with(gpioa.pa8); + let mut pwm_c2 = pwm_c2.with(gpioa.pa9); + let mut counter = dp.TIM2.counter_us(&clocks); - let max_duty = pwm.get_max_duty(); + let max_duty = pwm_c1.get_max_duty(); const N: usize = 50; let mut sin_a = [0_u16; N + 1]; @@ -38,24 +36,24 @@ fn main() -> ! { } counter.start(100.micros()).unwrap(); - pwm.enable(Channel::C1); - pwm.enable(Channel::C2); + pwm_c1.enable(); + pwm_c2.enable(); let mut i = 0; loop { if i == 0 { - pwm.set_duty(Channel::C2, 0); + pwm_c2.set_duty(0); } if i == 2 * N { - pwm.set_duty(Channel::C1, 0); + pwm_c1.set_duty(0); } if i < N { - pwm.set_duty(Channel::C1, sin_a[i]); + pwm_c1.set_duty(sin_a[i]); } else if i < 2 * N { - pwm.set_duty(Channel::C1, sin_a[2 * N - i]); + pwm_c1.set_duty(sin_a[2 * N - i]); } else if i < 3 * N { - pwm.set_duty(Channel::C2, sin_a[i - 2 * N]); + pwm_c2.set_duty(sin_a[i - 2 * N]); } else { - pwm.set_duty(Channel::C2, sin_a[4 * N - i]); + pwm_c2.set_duty(sin_a[4 * N - i]); } nb::block!(counter.wait()).unwrap(); i += 1; diff --git a/examples/pwm.rs b/examples/pwm.rs index 6c25211a..76e17f17 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -6,11 +6,7 @@ use panic_halt as _; use cortex_m_rt::entry; -use stm32f4xx_hal::{ - pac, - prelude::*, - timer::{Channel1, Channel2}, -}; +use stm32f4xx_hal::{pac, prelude::*}; #[entry] fn main() -> ! { @@ -20,10 +16,11 @@ fn main() -> ! { let clocks = rcc.cfgr.freeze(); let gpioa = dp.GPIOA.split(); - let channels = (Channel1::new(gpioa.pa8), Channel2::new(gpioa.pa9)); - let pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &clocks).split(); - let (mut ch1, _ch2) = pwm; + let (_, (ch1, ch2, ..)) = dp.TIM1.pwm_us(100.micros(), &clocks); + let mut ch1 = ch1.with(gpioa.pa8); + let mut _ch2 = ch2.with(gpioa.pa9); + let max_duty = ch1.get_max_duty(); ch1.set_duty(max_duty / 2); ch1.enable(); diff --git a/src/timer.rs b/src/timer.rs index 559ae782..3f7c3045 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -317,7 +317,7 @@ pub type CCR4 = CCR; pub struct DMAR(T); mod sealed { - use super::{BitFlags, Channel, Event, Flag, IdleState, Ocm, Polarity}; + use super::{BitFlags, Event, Flag, IdleState, Ocm, Polarity}; pub trait General { type Width: Into + From; fn max_auto_reload() -> u32; @@ -364,7 +364,8 @@ mod sealed { } pub trait WithPwm: WithPwmCommon { - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm); + fn preload_output_channel_in_mode(&mut self, c: u8, mode: Ocm); + fn freeze_output_channel(&mut self, c: u8); fn start_pwm(&mut self); } @@ -372,6 +373,11 @@ mod sealed { type Mms; fn master_mode(&mut self, mode: Self::Mms); } + + pub trait Split { + type Channels; + fn split() -> Self::Channels; + } } pub(crate) use sealed::{Advanced, General, MasterTimer, WithPwm, WithPwmCommon}; @@ -380,6 +386,27 @@ pub trait Instance: { } +use sealed::Split; +macro_rules! split { + ($TIM:ty: 1) => { + split!($TIM, C1); + }; + ($TIM:ty: 2) => { + split!($TIM, C1, C2); + }; + ($TIM:ty: 4) => { + split!($TIM, C1, C2, C3, C4); + }; + ($TIM:ty, $($C:ident),+) => { + impl Split for $TIM { + type Channels = ($(PwmChannelDisabled<$TIM, $C>,)+); + fn split() -> Self::Channels { + ($(PwmChannelDisabled::<_, $C>::new(),)+) + } + } + }; +} + macro_rules! hal { ($TIM:ty: [ $Timer:ident, @@ -389,6 +416,11 @@ macro_rules! hal { $(m: $timbase:ident,)? ]) => { impl Instance for $TIM { } + impl crate::Steal for $TIM { + unsafe fn steal() -> Self { + Self::steal() + } + } pub type $Timer = Timer<$TIM>; impl General for $TIM { @@ -577,6 +609,7 @@ macro_rules! hal { )? with_pwm!($TIM: $cnum $(, $aoe)?); + split!($TIM: $cnum); unsafe impl PeriAddress for CCR<$TIM, C> { #[inline(always)] fn address(&self) -> u32 { @@ -611,13 +644,13 @@ macro_rules! with_dmar { } macro_rules! with_pwm { - ($TIM:ty: [$($Cx:ident, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident;)+] $(, $aoe:ident)?) => { + ($TIM:ty: [$($Cx:literal, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident;)+] $(, $aoe:ident)?) => { impl WithPwm for $TIM { #[inline(always)] - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm) { - match channel { + fn preload_output_channel_in_mode(&mut self, c: u8, mode: Ocm) { + match c { $( - Channel::$Cx => { + $Cx => { self.$ccmrx_output() .modify(|_, w| w.$ocxpe().set_bit().$ocxm().set(mode as _) ); } @@ -626,6 +659,18 @@ macro_rules! with_pwm { _ => {}, } } + fn freeze_output_channel(&mut self, c: u8) { + match c { + $( + $Cx => { + self.$ccmrx_output() + .modify(|_, w| w.$ocxpe().clear_bit().$ocxm().set(Ocm::Frozen as _) ); + } + )+ + #[allow(unreachable_patterns)] + _ => {}, + } + } #[inline(always)] fn start_pwm(&mut self) { @@ -636,21 +681,21 @@ macro_rules! with_pwm { }; ($TIM:ty: 1) => { with_pwm!($TIM: [ - C1, ccmr1_output, oc1pe, oc1m; + 0, ccmr1_output, oc1pe, oc1m; ]); }; ($TIM:ty: 2) => { with_pwm!($TIM: [ - C1, ccmr1_output, oc1pe, oc1m; - C2, ccmr1_output, oc2pe, oc2m; + 0, ccmr1_output, oc1pe, oc1m; + 1, ccmr1_output, oc2pe, oc2m; ]); }; ($TIM:ty: 4 $(, $aoe:ident)?) => { with_pwm!($TIM: [ - C1, ccmr1_output, oc1pe, oc1m; - C2, ccmr1_output, oc2pe, oc2m; - C3, ccmr2_output, oc3pe, oc3m; - C4, ccmr2_output, oc4pe, oc4m; + 0, ccmr1_output, oc1pe, oc1m; + 1, ccmr1_output, oc2pe, oc2m; + 2, ccmr2_output, oc3pe, oc3m; + 3, ccmr2_output, oc4pe, oc4m; ] $(, $aoe)?); }; } diff --git a/src/timer/hal_02.rs b/src/timer/hal_02.rs index c622fab3..fd027d13 100644 --- a/src/timer/hal_02.rs +++ b/src/timer/hal_02.rs @@ -11,7 +11,7 @@ use fugit::{ExtU32Ceil, HertzU32 as Hertz, TimerDurationU32}; use void::Void; use super::{ - Channel, Counter, CounterHz, Delay, Error, Instance, Pins, Pwm, PwmChannel, PwmHz, SysCounter, + CPin, Counter, CounterHz, Delay, ErasedChannel, Error, Instance, PwmChannel, SysCounter, SysCounterHz, SysDelay, WithPwm, }; @@ -139,7 +139,9 @@ impl Cancel for SysCounter { } } -impl embedded_hal_02::PwmPin for PwmChannel { +impl, const C: u8, const COMP: bool, Otype> + embedded_hal_02::PwmPin for PwmChannel +{ type Duty = u16; fn disable(&mut self) { @@ -159,45 +161,23 @@ impl embedded_hal_02::PwmPin for PwmChanne } } -impl embedded_hal_02::Pwm for PwmHz -where - TIM: Instance + WithPwm, - PINS: Pins, -{ - type Channel = Channel; +impl embedded_hal_02::PwmPin for ErasedChannel { type Duty = u16; - type Time = Hertz; - fn enable(&mut self, channel: Self::Channel) { - self.enable(channel) - } - - fn disable(&mut self, channel: Self::Channel) { - self.disable(channel) + fn disable(&mut self) { + self.disable() } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty(channel) + fn enable(&mut self) { + self.enable() } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - self.set_duty(channel, duty) + fn get_duty(&self) -> Self::Duty { + self.get_duty() } - - /// If `0` returned means max_duty is 2^16 fn get_max_duty(&self) -> Self::Duty { self.get_max_duty() } - - fn get_period(&self) -> Self::Time { - self.get_period() - } - - fn set_period(&mut self, period: T) - where - T: Into, - { - self.set_period(period.into()) + fn set_duty(&mut self, duty: Self::Duty) { + self.set_duty(duty) } } @@ -268,45 +248,3 @@ impl Cancel for Counter { self.cancel() } } - -impl embedded_hal_02::Pwm for Pwm -where - TIM: Instance + WithPwm, - PINS: Pins, -{ - type Channel = Channel; - type Duty = u16; - type Time = TimerDurationU32; - - fn enable(&mut self, channel: Self::Channel) { - self.enable(channel) - } - - fn disable(&mut self, channel: Self::Channel) { - self.disable(channel) - } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty(channel) - } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - self.set_duty(channel, duty) - } - - /// If `0` returned means max_duty is 2^16 - fn get_max_duty(&self) -> Self::Duty { - self.get_max_duty() - } - - fn get_period(&self) -> Self::Time { - self.get_period() - } - - fn set_period(&mut self, period: T) - where - T: Into, - { - self.set_period(period.into()) - } -} diff --git a/src/timer/hal_1.rs b/src/timer/hal_1.rs index d022771d..1a333252 100644 --- a/src/timer/hal_1.rs +++ b/src/timer/hal_1.rs @@ -6,7 +6,7 @@ use core::convert::Infallible; use embedded_hal::delay::DelayNs; -use super::{Delay, Instance, PwmChannel, SysDelay, WithPwm}; +use super::{CPin, Delay, ErasedChannel, Instance, PwmChannel, SysDelay, WithPwm}; use fugit::ExtU32Ceil; impl DelayNs for SysDelay { @@ -33,11 +33,29 @@ impl DelayNs for Delay { } } -impl embedded_hal::pwm::ErrorType for PwmChannel { +impl, const C: u8, const COMP: bool, Otype> + embedded_hal::pwm::ErrorType for PwmChannel +{ type Error = Infallible; } -impl embedded_hal::pwm::SetDutyCycle for PwmChannel { +impl, const C: u8, const COMP: bool, Otype> + embedded_hal::pwm::SetDutyCycle for PwmChannel +{ + fn max_duty_cycle(&self) -> u16 { + self.get_max_duty() + } + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + self.set_duty(duty); + Ok(()) + } +} + +impl embedded_hal::pwm::ErrorType for ErasedChannel { + type Error = Infallible; +} + +impl embedded_hal::pwm::SetDutyCycle for ErasedChannel { fn max_duty_cycle(&self) -> u16 { self.get_max_duty() } diff --git a/src/timer/pwm.rs b/src/timer/pwm.rs index 86415be8..a8790cd4 100644 --- a/src/timer/pwm.rs +++ b/src/timer/pwm.rs @@ -7,578 +7,352 @@ //! //! Second one is based on [`Timer`] with dynamic internally calculated prescaler and require [`fugit::Hertz`] to set period. //! -//! You can [`split`](Pwm::split) any of those structures on independent `PwmChannel`s if you need that implement [`embedded_hal_02::PwmPin`] -//! but can't change PWM period. -//! +//! The main way to run PWM is calling [`FTimer::pwm`] with initial `period`/`frequency` corresponding PWM period. +//! This returns [`PwmManager`] and a tuple of all [`PwmChannel`]s supported by timer. //! Also there is [`PwmExt`] trait implemented on `pac::TIMx` to simplify creating new structure. //! -//! You need to pass one or tuple of channels with pins you plan to use and initial `time`/`frequency` corresponding PWM period. -//! Pins can be collected with [`ChannelBuilder`]s in sequence corresponding to the channel number. Smaller channel number first. -//! Each channel group can contain 1 or several main pins and 0, 1 or several complementary pins. -//! Start constructing channel with `new(first_main_pin)`. -//! Then use `.with(other_main_pin)` and `.with_complementary(other_complementary_pin)` accordingly -//! to add advanced pins on same channel. -//! -//! For example: -//! ```rust -//! let channels = ( -//! Channel1::new(gpioa.pa8), -//! Channel2::new(gpioa.pa9), // use Channel2OD` for `OpenDrain` pin -//! ); +//! ```rust,ignore +//! let (pwm_manager, (pwm_ch1, pwm_ch2, ..)) = dp.TIM1.pwm_us(100.micros(), &clocks); //! ``` -//! or +//! +//! Each `PwmChannel` implements [`embedded_hal::pwm::SetDutyCycle`]. +//! They are disabled. +//! To enable `PwmChannel` you need to pass one or more regular pins allowed by channel +//! using `with` or `with_open_drain`. +//! Also you can pass complementary pins by `.with_complementary(other_complementary_pin)`. +//! After connecting pins you can dynamically enable main or complementary channels with `enable` and `enable_complementary` +//! and change their polarity with `set_polarity` and `set_complementary_polarity`. +//! //! ```rust,ignore -//! let channels = Channel1::new(gpioa.pa8).with_complementary(gpioa.pa7); // (CH1, CHN1) +//! let mut pwm_c1 = pwm_c1.with(gpioa.pa8).with_complementary(gpioa.pa7); +//! pwm_c1.enable(); +//! pwm_c1.enable_complementary(); //! ``` //! -//! where `CHx` and `CHx_n` are main pins of PWM channel `x` and `CHNx` are complementary pins of PWM channel `x`. +//! By default `PwmChannel` contains information about connected pins to be possible to `release` them. +//! But you can `erase` this information to constuct [`ErasedChannel`] which can be collected to array. +//! Note that this operation is irreversible. //! -//! After creating structures you can dynamically enable main or complementary channels with `enable` and `enable_complementary` -//! and change their polarity with `set_polarity` and `set_complementary_polarity`. +//! `PwmManager` allows you to change PWM `period`/`frequency` and also has methods for advanced PWM control. +use super::sealed::Split; use super::{ - compute_arr_presc, Advanced, CPin, Channel, FTimer, IdleState, Instance, NCPin, Ocm, Polarity, - Timer, WithPwm, + compute_arr_presc, Advanced, CPin, FTimer, IdleState, Instance, NCPin, Ocm, Polarity, Timer, + WithPwm, }; pub use super::{Ch, C1, C2, C3, C4}; use crate::gpio::{OpenDrain, PushPull}; use crate::rcc::Clocks; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use fugit::{HertzU32 as Hertz, TimerDurationU32}; -pub type Channel1 = ChannelBuilder; -pub type Channel2 = ChannelBuilder; -pub type Channel3 = ChannelBuilder; -pub type Channel4 = ChannelBuilder; -pub type Channel1OD = ChannelBuilder; -pub type Channel2OD = ChannelBuilder; -pub type Channel3OD = ChannelBuilder; -pub type Channel4OD = ChannelBuilder; - -pub struct ChannelBuilder { - pub(super) _tim: PhantomData<(TIM, Otype)>, -} - -impl ChannelBuilder -where - TIM: CPin, -{ - pub fn new(pin: impl Into>) -> Self { - let _pin = pin.into(); - Self { _tim: PhantomData } - } -} -impl ChannelBuilder -where - TIM: CPin, -{ - pub fn with(self, pin: impl Into>) -> Self { - let _pin = pin.into(); - self - } -} -impl ChannelBuilder -where - TIM: NCPin, -{ - pub fn with_complementary( - self, - pin: impl Into>, - ) -> ChannelBuilder { - let _pin = pin.into(); - ChannelBuilder { _tim: PhantomData } - } -} - -impl sealed::Split - for ChannelBuilder -{ - type Channels = PwmChannel; - fn split() -> Self::Channels { - PwmChannel::new() - } -} - -mod sealed { - pub trait Split { - type Channels; - fn split() -> Self::Channels; - } - macro_rules! split { - ($($T:ident),+) => { - impl<$($T),+> Split for ($($T),+) - where - $($T: Split,)+ - { - type Channels = ($($T::Channels),+); - fn split() -> Self::Channels { - ($($T::split()),+) - } - } - }; - } - split!(T1, T2); - split!(T1, T2, T3); - split!(T1, T2, T3, T4); -} -pub trait Pins: sealed::Split { - const C1: bool = false; - const C2: bool = false; - const C3: bool = false; - const C4: bool = false; - const NC1: bool = false; - const NC2: bool = false; - const NC3: bool = false; - const NC4: bool = false; - - fn check_used(c: Channel) -> Channel { - if (c == Channel::C1 && Self::C1) - || (c == Channel::C2 && Self::C2) - || (c == Channel::C3 && Self::C3) - || (c == Channel::C4 && Self::C4) - { - c - } else { - panic!("Unused channel") - } - } - - fn check_complementary_used(c: Channel) -> Channel { - if (c == Channel::C1 && Self::NC1) - || (c == Channel::C2 && Self::NC2) - || (c == Channel::C3 && Self::NC3) - || (c == Channel::C4 && Self::NC4) - { - c - } else { - panic!("Unused channel") - } - } -} - -macro_rules! pins_impl { - ( $( $(($Otype:ident, $ENCHX:ident, $COMP:ident)),+; )+ ) => { - $( - #[allow(unused_parens)] - impl Pins for ($(ChannelBuilder),+) { - $( - const $ENCHX: bool = true; - const $COMP: bool = $COMP; - )+ - } - )+ - }; -} - -pins_impl!( - (O1, C1, NC1), (O2, C2, NC2), (O3, C3, NC3), (O4, C4, NC4); - - (O2, C2, NC2), (O3, C3, NC3), (O4, C4, NC4); - (O1, C1, NC1), (O3, C3, NC3), (O4, C4, NC4); - (O1, C1, NC1), (O2, C2, NC2), (O4, C4, NC4); - (O1, C1, NC1), (O2, C2, NC2), (O3, C3, NC3); - - (O3, C3, NC3), (O4, C4, NC4); - (O2, C2, NC2), (O4, C4, NC4); - (O2, C2, NC2), (O3, C3, NC3); - (O1, C1, NC1), (O4, C4, NC4); - (O1, C1, NC1), (O3, C3, NC3); - (O1, C1, NC1), (O2, C2, NC2); - - (O1, C1, NC1); - (O2, C2, NC2); - (O3, C3, NC3); - (O4, C4, NC4); -); - -pub struct PwmChannel { - pub(super) _tim: PhantomData, -} - pub trait PwmExt where - Self: Sized + Instance + WithPwm, + Self: Sized + Instance + WithPwm + Split, { - fn pwm( + fn pwm( self, - pins: PINS, time: TimerDurationU32, clocks: &Clocks, - ) -> Pwm - where - PINS: Pins; + ) -> (PwmManager, Self::Channels); - fn pwm_hz(self, pins: PINS, freq: Hertz, clocks: &Clocks) -> PwmHz - where - PINS: Pins; + fn pwm_hz(self, freq: Hertz, clocks: &Clocks) -> (PwmHzManager, Self::Channels); - fn pwm_us( + fn pwm_us( self, - pins: PINS, time: TimerDurationU32<1_000_000>, clocks: &Clocks, - ) -> Pwm - where - PINS: Pins, - { - self.pwm::<_, 1_000_000>(pins, time, clocks) + ) -> (PwmManager, Self::Channels) { + self.pwm::<1_000_000>(time, clocks) } } impl PwmExt for TIM where - Self: Sized + Instance + WithPwm, + Self: Sized + Instance + WithPwm + Split, { - fn pwm( + fn pwm( self, - pins: PINS, time: TimerDurationU32, clocks: &Clocks, - ) -> Pwm - where - PINS: Pins, - { - FTimer::::new(self, clocks).pwm(pins, time) - } - - fn pwm_hz(self, pins: PINS, time: Hertz, clocks: &Clocks) -> PwmHz - where - PINS: Pins, - { - Timer::new(self, clocks).pwm_hz(pins, time) + ) -> (PwmManager, Self::Channels) { + FTimer::::new(self, clocks).pwm(time) } -} -impl PwmChannel { - pub(crate) fn new() -> Self { - Self { - _tim: core::marker::PhantomData, - } + fn pwm_hz(self, time: Hertz, clocks: &Clocks) -> (PwmHzManager, Self::Channels) { + Timer::new(self, clocks).pwm_hz(time) } } -impl PwmChannel { - /// Disable PWM channel - #[inline] - pub fn disable(&mut self) { - TIM::enable_channel(C, false); - } - - /// Enable PWM channel - #[inline] - pub fn enable(&mut self) { - TIM::enable_channel(C, true); - } - - /// Set PWM channel polarity - #[inline] - pub fn set_polarity(&mut self, p: Polarity) { - TIM::set_channel_polarity(C, p); - } +impl Timer { + pub fn pwm_hz(mut self, freq: Hertz) -> (PwmHzManager, TIM::Channels) { + // The reference manual is a bit ambiguous about when enabling this bit is really + // necessary, but since we MUST enable the preload for the output channels then we + // might as well enable for the auto-reload too + self.tim.enable_preload(true); - /// Get PWM channel duty cycle - #[inline] - pub fn get_duty(&self) -> u16 { - TIM::read_cc_value(C) as u16 - } + let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw()); + self.tim.set_prescaler(psc); + self.tim.set_auto_reload(arr).unwrap(); - /// Get the maximum duty cycle value of the PWM channel - /// - /// If `0` returned means max_duty is 2^16 - #[inline] - pub fn get_max_duty(&self) -> u16 { - (TIM::read_auto_reload() as u16).wrapping_add(1) - } + // Trigger update event to load the registers + self.tim.trigger_update(); - /// Set PWM channel duty cycle - #[inline] - pub fn set_duty(&mut self, duty: u16) { - TIM::set_cc_value(C, duty as u32) - } + self.tim.start_pwm(); - /// Set complementary PWM channel polarity - #[inline] - pub fn set_complementary_polarity(&mut self, p: Polarity) { - TIM::set_nchannel_polarity(C, p); + (PwmHzManager { timer: self }, TIM::split()) } } -impl PwmChannel { - /// Disable complementary PWM channel - #[inline] - pub fn disable_complementary(&mut self) { - TIM::enable_nchannel(C, false); - } +impl FTimer { + pub fn pwm(mut self, time: TimerDurationU32) -> (PwmManager, TIM::Channels) { + // The reference manual is a bit ambiguous about when enabling this bit is really + // necessary, but since we MUST enable the preload for the output channels then we + // might as well enable for the auto-reload too + self.tim.enable_preload(true); - /// Enable complementary PWM channel - #[inline] - pub fn enable_complementary(&mut self) { - TIM::enable_nchannel(C, true); - } + self.tim.set_auto_reload(time.ticks() - 1).unwrap(); - /// Set PWM channel idle state - #[inline] - pub fn set_idle_state(&mut self, s: IdleState) { - TIM::idle_state(C, false, s); - } + // Trigger update event to load the registers + self.tim.trigger_update(); - /// Set complementary PWM channel idle state - #[inline] - pub fn set_complementary_idle_state(&mut self, s: IdleState) { - TIM::idle_state(C, true, s); + self.tim.start_pwm(); + + (PwmManager { timer: self }, TIM::split()) } } -pub struct PwmHz -where - TIM: Instance + WithPwm, - PINS: Pins, -{ - timer: Timer, - _pins: PhantomData, +pub struct PwmChannelDisabled { + pub(super) tim: TIM, } -impl PwmHz -where - TIM: Instance + WithPwm, - PINS: Pins, -{ - pub fn release(mut self) -> Timer { - // stop timer - self.tim.cr1_reset(); - self.timer - } - - pub fn split(self) -> PINS::Channels { - PINS::split() +impl PwmChannelDisabled { + pub(crate) fn new() -> Self { + Self { + tim: unsafe { TIM::steal() }, + } } } - -impl Deref for PwmHz +impl PwmChannelDisabled where - TIM: Instance + WithPwm, - PINS: Pins, + TIM: CPin, { - type Target = Timer; - fn deref(&self) -> &Self::Target { - &self.timer + pub fn with( + mut self, + pin: impl Into>, + ) -> PwmChannel { + self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1); + PwmChannel { + tim: self.tim, + lines: Lines::One(pin.into()), + } } -} - -impl DerefMut for PwmHz -where - TIM: Instance + WithPwm, - PINS: Pins, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.timer + pub fn with_open_drain( + mut self, + pin: impl Into>, + ) -> PwmChannel { + self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1); + PwmChannel { + tim: self.tim, + lines: Lines::One(pin.into()), + } } } -impl Timer { - pub fn pwm_hz(mut self, _pins: PINS, freq: Hertz) -> PwmHz - where - PINS: Pins, - { - if PINS::C1 { - self.tim - .preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1); - } - if PINS::C2 && TIM::CH_NUMBER > 1 { - self.tim - .preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1); - } - if PINS::C3 && TIM::CH_NUMBER > 2 { - self.tim - .preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1); - } - if PINS::C4 && TIM::CH_NUMBER > 3 { - self.tim - .preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1); +#[derive(Debug)] +pub enum Lines

{ + One(P), + Two(P, P), + Three(P, P, P), + Four(P, P, P, P), +} +impl

Lines

{ + pub fn and(self, pin: P) -> Self { + match self { + Self::One(p) => Self::Two(p, pin), + Self::Two(p1, p2) => Self::Three(p1, p2, pin), + Self::Three(p1, p2, p3) => Self::Four(p1, p2, p3, pin), + Self::Four(_, _, _, _) => unreachable!(), } + } +} - // The reference manual is a bit ambiguous about when enabling this bit is really - // necessary, but since we MUST enable the preload for the output channels then we - // might as well enable for the auto-reload too - self.tim.enable_preload(true); - - let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw()); - self.tim.set_prescaler(psc); - self.tim.set_auto_reload(arr).unwrap(); - - // Trigger update event to load the registers - self.tim.trigger_update(); - - self.tim.start_pwm(); +pub struct PwmChannel, const C: u8, const COMP: bool = false, Otype = PushPull> { + pub(super) tim: TIM, + lines: Lines>, + // TODO: add complementary pins +} - PwmHz { - timer: self, - _pins: PhantomData, +impl, const C: u8, const COMP: bool, Otype> + PwmChannel +{ + pub const fn channel(&self) -> u8 { + C + } + pub fn release(mut self) -> (PwmChannelDisabled, Lines>) { + self.tim.freeze_output_channel(C); + (PwmChannelDisabled { tim: self.tim }, self.lines) + } + pub fn erase(self) -> ErasedChannel { + ErasedChannel { + _tim: self.tim, + channel: C, } } } - -impl PwmHz -where - TIM: Instance + WithPwm, - PINS: Pins, +impl, const C: u8, const COMP: bool, Otype> + PwmChannel { - /// Enable PWM output of the timer on channel `channel` - #[inline] - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) + pub fn with(self, pin: impl Into>) -> Self { + Self { + tim: self.tim, + lines: self.lines.and(pin.into()), + } } - - /// Disable PWM output of the timer on channel `channel` - #[inline] - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) +} +impl + NCPin, const C: u8, const COMP: bool, Otype> + PwmChannel +{ + pub fn with_complementary( + self, + pin: impl Into>, + ) -> PwmChannel { + let _pin = pin.into(); + PwmChannel { + tim: self.tim, + lines: self.lines, + } } +} - /// Set the polarity of the active state for the primary PWM output of the timer on channel `channel` - #[inline] - pub fn set_polarity(&mut self, channel: Channel, p: Polarity) { - TIM::set_channel_polarity(PINS::check_used(channel) as u8, p); - } +pub struct ErasedChannel { + _tim: TIM, + channel: u8, +} - /// Get the current duty cycle of the timer on channel `channel` - #[inline] - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 +impl ErasedChannel { + pub const fn channel(&self) -> u8 { + self.channel } +} - /// Set the duty cycle of the timer on channel `channel` - #[inline] - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty as u32) - } +macro_rules! ch_impl { + () => { + /// Disable PWM channel + #[inline] + pub fn disable(&mut self) { + TIM::enable_channel(self.channel(), false); + } - /// Get the maximum duty cycle value of the timer - /// - /// If `0` returned means max_duty is 2^16 - pub fn get_max_duty(&self) -> u16 { - (TIM::read_auto_reload() as u16).wrapping_add(1) - } + /// Enable PWM channel + #[inline] + pub fn enable(&mut self) { + TIM::enable_channel(self.channel(), true); + } - /// Get the PWM frequency of the timer in Hertz - pub fn get_period(&self) -> Hertz { - let clk = self.clk; - let psc = self.tim.read_prescaler() as u32; - let arr = TIM::read_auto_reload(); + /// Get PWM channel duty cycle + #[inline] + pub fn get_duty(&self) -> u16 { + TIM::read_cc_value(self.channel()) as u16 + } - // Length in ms of an internal clock pulse - clk / ((psc + 1) * (arr + 1)) - } + /// Get the maximum duty cycle value of the PWM channel + /// + /// If `0` returned means max_duty is 2^16 + #[inline] + pub fn get_max_duty(&self) -> u16 { + (TIM::read_auto_reload() as u16).wrapping_add(1) + } - /// Set the PWM frequency for the timer in Hertz - pub fn set_period(&mut self, period: Hertz) { - let clk = self.clk; + /// Set PWM channel duty cycle + #[inline] + pub fn set_duty(&mut self, duty: u16) { + TIM::set_cc_value(self.channel(), duty as u32) + } - let (psc, arr) = compute_arr_presc(period.raw(), clk.raw()); - self.tim.set_prescaler(psc); - self.tim.set_auto_reload(arr).unwrap(); - self.tim.cnt_reset(); - } + /// Set PWM channel polarity + #[inline] + pub fn set_polarity(&mut self, p: Polarity) { + TIM::set_channel_polarity(self.channel(), p); + } - /// Set the polarity of the active state for the complementary PWM output of the advanced timer on channel `channel` - #[inline] - pub fn set_complementary_polarity(&mut self, channel: Channel, p: Polarity) { - TIM::set_nchannel_polarity(PINS::check_complementary_used(channel) as u8, p); - } + /// Set complementary PWM channel polarity + #[inline] + pub fn set_complementary_polarity(&mut self, p: Polarity) { + TIM::set_nchannel_polarity(self.channel(), p); + } + }; } -impl PwmHz -where - TIM: Instance + WithPwm + Advanced, - PINS: Pins, -{ - /// Enable complementary PWM output of the timer on channel `channel` - #[inline] - pub fn enable_complementary(&mut self, channel: Channel) { - TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, true) - } +macro_rules! chN_impl { + () => { + /// Disable complementary PWM channel + #[inline] + pub fn disable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), false); + } - /// Disable complementary PWM output of the timer on channel `channel` - #[inline] - pub fn disable_complementary(&mut self, channel: Channel) { - TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, false) - } + /// Enable complementary PWM channel + #[inline] + pub fn enable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), true); + } - /// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states - /// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state is called 'dead time'. - /// - /// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by - /// short circuit in the moment of switching. - #[inline] - pub fn set_dead_time(&mut self, dts_ticks: u16) { - let bits = pack_ceil_dead_time(dts_ticks); - TIM::set_dtg_value(bits); - } + /// Set PWM channel idle state + #[inline] + pub fn set_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), false, s); + } - /// Set raw dead time (DTG) bits - /// - /// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of - /// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics - #[inline] - pub fn set_dead_time_bits(&mut self, bits: u8) { - TIM::set_dtg_value(bits); - } + /// Set complementary PWM channel idle state + #[inline] + pub fn set_complementary_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), true, s); + } + }; +} - /// Return dead time for complementary pins in the unit of DTS ticks - #[inline] - pub fn get_dead_time(&self) -> u16 { - unpack_dead_time(TIM::read_dtg_value()) - } +impl, const C: u8, const COMP: bool, Otype> + PwmChannel +{ + ch_impl!(); +} - /// Get raw dead time (DTG) bits - #[inline] - pub fn get_dead_time_bits(&self) -> u8 { - TIM::read_dtg_value() - } +impl, const C: u8, Otype> + PwmChannel +{ + chN_impl!(); +} - /// Set the pin idle state - #[inline] - pub fn set_idle_state(&mut self, channel: Channel, s: IdleState) { - TIM::idle_state(PINS::check_used(channel) as u8, false, s); - } +impl ErasedChannel { + ch_impl!(); +} - /// Set the complementary pin idle state - #[inline] - pub fn set_complementary_idle_state(&mut self, channel: Channel, s: IdleState) { - TIM::idle_state(PINS::check_complementary_used(channel) as u8, true, s); - } +impl ErasedChannel { + chN_impl!(); } -pub struct Pwm +pub struct PwmManager where TIM: Instance + WithPwm, - PINS: Pins, { - timer: FTimer, - _pins: PhantomData, + pub(super) timer: FTimer, } -impl Pwm +impl PwmManager where - TIM: Instance + WithPwm, - PINS: Pins, + TIM: Instance + WithPwm + Split, { - pub fn split(self) -> PINS::Channels { - PINS::split() - } - - pub fn release(mut self) -> FTimer { + pub fn release(mut self, _channels: TIM::Channels) -> FTimer { // stop counter self.tim.cr1_reset(); self.timer } } -impl Deref for Pwm +impl Deref for PwmManager where TIM: Instance + WithPwm, - PINS: Pins, { type Target = FTimer; fn deref(&self) -> &Self::Target { @@ -586,103 +360,56 @@ where } } -impl DerefMut for Pwm +impl DerefMut for PwmManager where TIM: Instance + WithPwm, - PINS: Pins, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.timer } } -impl FTimer { - pub fn pwm(mut self, _pins: PINS, time: TimerDurationU32) -> Pwm - where - PINS: Pins, - { - if PINS::C1 { - self.tim - .preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1); - } - if PINS::C2 && TIM::CH_NUMBER > 1 { - self.tim - .preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1); - } - if PINS::C3 && TIM::CH_NUMBER > 2 { - self.tim - .preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1); - } - if PINS::C4 && TIM::CH_NUMBER > 3 { - self.tim - .preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1); - } - - // The reference manual is a bit ambiguous about when enabling this bit is really - // necessary, but since we MUST enable the preload for the output channels then we - // might as well enable for the auto-reload too - self.tim.enable_preload(true); - - self.tim.set_auto_reload(time.ticks() - 1).unwrap(); - - // Trigger update event to load the registers - self.tim.trigger_update(); - - self.tim.start_pwm(); +pub struct PwmHzManager +where + TIM: Instance + WithPwm, +{ + pub(super) timer: Timer, +} - Pwm { - timer: self, - _pins: PhantomData, - } +impl PwmHzManager +where + TIM: Instance + WithPwm + Split, +{ + pub fn release(mut self, _channels: TIM::Channels) -> Timer { + // stop timer + self.tim.cr1_reset(); + self.timer } } -impl Pwm +impl Deref for PwmHzManager where TIM: Instance + WithPwm, - PINS: Pins, { - /// Enable PWM output of the timer on channel `channel` - #[inline] - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) - } - - /// Disable PWM output of the timer on channel `channel` - #[inline] - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) - } - - /// Set the polarity of the active state for the primary PWM output of the timer on channel `channel` - #[inline] - pub fn set_polarity(&mut self, channel: Channel, p: Polarity) { - TIM::set_channel_polarity(PINS::check_used(channel) as u8, p); - } - - /// Get the current duty cycle of the timer on channel `channel` - #[inline] - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 - } - /// Get the current duty cycle of the timer on channel `channel` and convert to a duration - #[inline] - pub fn get_duty_time(&self, channel: Channel) -> TimerDurationU32 { - TimerDurationU32::from_ticks(TIM::read_cc_value(PINS::check_used(channel) as u8)) - } - - /// Set the duty cycle of the timer on channel `channel` - #[inline] - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into()) + type Target = Timer; + fn deref(&self) -> &Self::Target { + &self.timer } +} - /// Set the duty cycle of the timer on channel `channel` from a duration - #[inline] - pub fn set_duty_time(&mut self, channel: Channel, duty: TimerDurationU32) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty.ticks()) +impl DerefMut for PwmHzManager +where + TIM: Instance + WithPwm, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.timer } +} +impl PwmManager +where + TIM: Instance + WithPwm, +{ /// Get the maximum duty cycle value of the timer /// /// If `0` returned means max_duty is 2^16 @@ -700,74 +427,88 @@ where self.tim.set_auto_reload(period.ticks() - 1).unwrap(); self.tim.cnt_reset(); } - - /// Set the polarity of the active state for the complementary PWM output of the advanced timer on channel `channel` - #[inline] - pub fn set_complementary_polarity(&mut self, channel: Channel, p: Polarity) { - TIM::set_channel_polarity(PINS::check_complementary_used(channel) as u8, p); - } } -impl Pwm +impl PwmHzManager where - TIM: Instance + WithPwm + Advanced, - PINS: Pins, + TIM: Instance + WithPwm, { - /// Enable complementary PWM output of the timer on channel `channel` - #[inline] - pub fn enable_complementary(&mut self, channel: Channel) { - TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, true) + /// Get the maximum duty cycle value of the timer + /// + /// If `0` returned means max_duty is 2^16 + pub fn get_max_duty(&self) -> u16 { + (TIM::read_auto_reload() as u16).wrapping_add(1) } - /// Disable complementary PWM output of the timer on channel `channel` - #[inline] - pub fn disable_complementary(&mut self, channel: Channel) { - TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, false) - } + /// Get the PWM frequency of the timer in Hertz + pub fn get_period(&self) -> Hertz { + let clk = self.clk; + let psc = self.tim.read_prescaler() as u32; + let arr = TIM::read_auto_reload(); - /// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states - /// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state is called 'dead time'. - /// - /// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by - /// short circuit in the moment of switching. - #[inline] - pub fn set_dead_time(&mut self, dts_ticks: u16) { - let bits = pack_ceil_dead_time(dts_ticks); - TIM::set_dtg_value(bits); + // Length in ms of an internal clock pulse + clk / ((psc + 1) * (arr + 1)) } - /// Set raw dead time (DTG) bits - /// - /// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of - /// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics - #[inline] - pub fn set_dead_time_bits(&mut self, bits: u8) { - TIM::set_dtg_value(bits); - } + /// Set the PWM frequency for the timer in Hertz + pub fn set_period(&mut self, period: Hertz) { + let clk = self.clk; - /// Return dead time for complementary pins in the unit of DTS ticks - #[inline] - pub fn get_dead_time(&self) -> u16 { - unpack_dead_time(TIM::read_dtg_value()) + let (psc, arr) = compute_arr_presc(period.raw(), clk.raw()); + self.tim.set_prescaler(psc); + self.tim.set_auto_reload(arr).unwrap(); + self.tim.cnt_reset(); } +} - /// Get raw dead time (DTG) bits - #[inline] - pub fn get_dead_time_bits(&self) -> u8 { - TIM::read_dtg_value() - } +macro_rules! impl_advanced { + () => { + /// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states + /// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state is called 'dead time'. + /// + /// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by + /// short circuit in the moment of switching. + #[inline] + pub fn set_dead_time(&mut self, dts_ticks: u16) { + let bits = pack_ceil_dead_time(dts_ticks); + TIM::set_dtg_value(bits); + } - /// Set the pin idle state - #[inline] - pub fn set_idle_state(&mut self, channel: Channel, s: IdleState) { - TIM::idle_state(PINS::check_used(channel) as u8, false, s); - } + /// Set raw dead time (DTG) bits + /// + /// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of + /// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics + #[inline] + pub fn set_dead_time_bits(&mut self, bits: u8) { + TIM::set_dtg_value(bits); + } - /// Set the complementary pin idle state - #[inline] - pub fn set_complementary_idle_state(&mut self, channel: Channel, s: IdleState) { - TIM::idle_state(PINS::check_complementary_used(channel) as u8, true, s); - } + /// Return dead time for complementary pins in the unit of DTS ticks + #[inline] + pub fn get_dead_time(&self) -> u16 { + unpack_dead_time(TIM::read_dtg_value()) + } + + /// Get raw dead time (DTG) bits + #[inline] + pub fn get_dead_time_bits(&self) -> u8 { + TIM::read_dtg_value() + } + }; +} + +impl PwmManager +where + TIM: Instance + WithPwm + Advanced, +{ + impl_advanced!(); +} + +impl PwmHzManager +where + TIM: Instance + WithPwm + Advanced, +{ + impl_advanced!(); } /// Convert number dead time ticks to raw DTG register bits.