diff --git a/CHANGELOG.md b/CHANGELOG.md index c59295c3..385d0350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Temporary replace `stm32f1` with `stm32f1-staging` [#503] - `Spi` now takes `Option` for `SCK`, `MISO`, `MOSI` [#514] - move `Qei` mod inside `pwm_input` mod [#516] +- move pin connecting to timer channels after `Pwm` initialization ### Changed diff --git a/examples/pwm.rs b/examples/pwm.rs index d15b899a..424db1d1 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -9,12 +9,7 @@ use panic_halt as _; use cortex_m::asm; use cortex_m_rt::entry; -use stm32f1xx_hal::{ - pac, - prelude::*, - time::ms, - timer::{Channel, Tim2NoRemap}, -}; +use stm32f1xx_hal::{pac, prelude::*, time::ms}; #[entry] fn main() -> ! { @@ -25,90 +20,70 @@ fn main() -> ! { let clocks = rcc.cfgr.freeze(&mut flash.acr); - let mut afio = p.AFIO.constrain(); - - let mut gpioa = p.GPIOA.split(); - // let mut gpiob = p.GPIOB.split(); + let gpioa = p.GPIOA.split(); + // let gpiob = p.GPIOB.split(); // TIM2 - let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); - let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); - let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); + let c1 = gpioa.pa0; + let c2 = gpioa.pa1; + let c3 = gpioa.pa2; // If you don't want to use all channels, just leave some out - // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); - let pins = (c1, c2, c3); + // let c4 = gpioa.pa3; // TIM3 - // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl); - // let c2 = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); - // let c3 = gpiob.pb0.into_alternate_push_pull(&mut gpiob.crl); - // let c4 = gpiob.pb1.into_alternate_push_pull(&mut gpiob.crl); + // let c1 = gpioa.pa6; + // let c2 = gpioa.pa7; + // let c3 = gpiob.pb0; + // let c4 = gpiob.pb1; // TIM4 (Only available with the "medium" density feature) - // let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - // let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl); - // let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); - // let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); + // let c1 = gpiob.pb6; + // let c2 = gpiob.pb7; + // let c3 = gpiob.pb8; + // let c4 = gpiob.pb9; //let mut pwm = - // Timer::new(p.TIM2, &clocks).pwm_hz::(pins, &mut afio.mapr, 1.kHz()); + // Timer::new(p.TIM2, &clocks).pwm_hz(pins, 1.kHz()); // or - let mut pwm = p - .TIM2 - .pwm_hz::(pins, &mut afio.mapr, 1.kHz(), &clocks); + let (mut pwm_mgr, (pwm_c1, pwm_c2, pwm_c3, ..)) = p.TIM2.pwm_hz(1.kHz(), &clocks); // Enable clock on each of the channels - pwm.enable(Channel::C1); - pwm.enable(Channel::C2); - pwm.enable(Channel::C3); + let mut c1 = pwm_c1.with(c1); + c1.enable(); + let mut c2 = pwm_c2.with(c2); + c2.enable(); + let mut c3 = pwm_c3.with(c3); + c3.enable(); //// Operations affecting all defined channels on the Timer // Adjust period to 0.5 seconds - pwm.set_period(ms(500).into_rate()); + pwm_mgr.set_period(ms(500).into_rate()); asm::bkpt(); // Return to the original frequency - pwm.set_period(1.kHz()); + pwm_mgr.set_period(1.kHz()); asm::bkpt(); - let max = pwm.get_max_duty(); + let max = pwm_mgr.get_max_duty(); //// Operations affecting single channels can be accessed through //// the Pwm object or via dereferencing to the pin. // Use the Pwm object to set C3 to full strength - pwm.set_duty(Channel::C3, max); + c3.set_duty(max); asm::bkpt(); // Use the Pwm object to set C3 to be dim - pwm.set_duty(Channel::C3, max / 4); + c3.set_duty(max / 4); asm::bkpt(); // Use the Pwm object to set C3 to be zero - pwm.set_duty(Channel::C3, 0); - - asm::bkpt(); - - // Extract the PwmChannel for C3 - let mut pwm_channel = pwm.split().2; - - // Use the PwmChannel object to set C3 to be full strength - pwm_channel.set_duty(max); - - asm::bkpt(); - - // Use the PwmChannel object to set C3 to be dim - pwm_channel.set_duty(max / 4); - - asm::bkpt(); - - // Use the PwmChannel object to set C3 to be zero - pwm_channel.set_duty(0); + c3.set_duty(0); asm::bkpt(); diff --git a/examples/pwm_custom.rs b/examples/pwm_custom.rs index 95d3e2f0..7a5ebdca 100644 --- a/examples/pwm_custom.rs +++ b/examples/pwm_custom.rs @@ -8,7 +8,7 @@ use panic_halt as _; use cortex_m::asm; -use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; +use stm32f1xx_hal::{pac, prelude::*}; use cortex_m_rt::entry; @@ -30,30 +30,31 @@ fn main() -> ! { let p0 = pb4.into_alternate_push_pull(&mut gpiob.crl); let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); - let pwm = Timer::new(p.TIM3, &clocks).pwm_hz((p0, p1), &mut afio.mapr, 1.kHz()); + let (pwm, pwm_channels) = p.TIM3.remap(&mut afio.mapr).pwm_hz(1.kHz(), &clocks); let max = pwm.get_max_duty(); - let mut pwm_channels = pwm.split(); + let mut c1 = pwm_channels.0.with(p0); + let mut c2 = pwm_channels.1.with(p1); // Enable the individual channels - pwm_channels.0.enable(); - pwm_channels.1.enable(); + c1.enable(); + c2.enable(); // full - pwm_channels.0.set_duty(max); - pwm_channels.1.set_duty(max); + c1.set_duty(max); + c2.set_duty(max); asm::bkpt(); // dim - pwm_channels.1.set_duty(max / 4); + c2.set_duty(max / 4); asm::bkpt(); // zero - pwm_channels.0.set_duty(0); - pwm_channels.1.set_duty(0); + c1.set_duty(0); + c2.set_duty(0); asm::bkpt(); diff --git a/src/lib.rs b/src/lib.rs index 35e1bbdf..7d131396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,3 +157,20 @@ mod sealed { pub trait Sealed {} } use sealed::Sealed; + +pub trait Steal { + /// Steal an instance of this peripheral + /// + /// # Safety + /// + /// Ensure that the new instance of the peripheral cannot be used in a way + /// that may race with any existing instances, for example by only + /// accessing read-only or write-only registers, or by consuming the + /// original peripheral and using critical sections to coordinate + /// access between multiple new instances. + /// + /// Additionally the HAL may rely on only one + /// peripheral instance existing to ensure memory safety; ensure + /// no stolen instances are passed to such software. + unsafe fn steal() -> Self; +} diff --git a/src/timer.rs b/src/timer.rs index 997507ce..f9bba8da 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -50,6 +50,7 @@ use crate::bb; use crate::pac::{self, DBGMCU as DBG, RCC}; +use crate::afio::Rmp; use crate::rcc::{self, Clocks}; use core::convert::TryFrom; @@ -62,10 +63,8 @@ use crate::time::Hertz; pub mod monotonic; #[cfg(feature = "rtic")] pub use monotonic::*; -pub(crate) mod pins; -pub mod pwm_input; -pub use pins::*; pub mod delay; +pub mod pwm_input; pub use delay::*; pub mod counter; pub use counter::*; @@ -90,6 +89,29 @@ pub enum Channel { C4 = 3, } +pub use crate::afio::{TimC, TimNC}; + +/// Channel wrapper +pub struct Ch; +pub const C1: u8 = 0; +pub const C2: u8 = 1; +pub const C3: u8 = 2; +pub const C4: u8 = 3; + +/// Enum for IO polarity +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Polarity { + ActiveHigh, + ActiveLow, +} + +/// Output Idle state +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum IdleState { + Reset, + Set, +} + /// Interrupt events #[derive(Clone, Copy, PartialEq, Eq)] pub enum SysEvent { @@ -258,8 +280,11 @@ pub enum Ocm { PwmMode2 = 7, } +// Center-aligned mode selection +pub use pac::tim1::cr1::CMS as CenterAlignedMode; + mod sealed { - use super::{Channel, Event, Ocm, DBG}; + use super::{CenterAlignedMode, Event, IdleState, Ocm, Polarity, DBG}; pub trait General { type Width: Into + From; fn max_auto_reload() -> u32; @@ -267,8 +292,7 @@ mod sealed { fn set_auto_reload(&mut self, arr: u32) -> Result<(), super::Error>; fn read_auto_reload() -> u32; fn enable_preload(&mut self, b: bool); - fn enable_counter(&mut self); - fn disable_counter(&mut self); + fn enable_counter(&mut self, b: bool); fn is_counter_enabled(&self) -> bool; fn reset_counter(&mut self); fn set_prescaler(&mut self, psc: u16); @@ -280,305 +304,344 @@ mod sealed { fn read_count(&self) -> Self::Width; fn start_one_pulse(&mut self); fn cr1_reset(&mut self); + fn cnt_reset(&mut self); fn stop_in_debug(&mut self, dbg: &mut DBG, state: bool); } - pub trait WithPwm: General { + pub trait WithPwmCommon: General { const CH_NUMBER: u8; + const COMP_CH_NUMBER: u8; fn read_cc_value(channel: u8) -> u32; fn set_cc_value(channel: u8, value: u32); - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm); - fn start_pwm(&mut self); fn enable_channel(channel: u8, b: bool); + fn set_channel_polarity(channel: u8, p: Polarity); + fn set_nchannel_polarity(channel: u8, p: Polarity); + } + + pub trait Advanced: WithPwmCommon { + fn enable_nchannel(channel: u8, b: bool); + fn set_dtg_value(value: u8); + fn read_dtg_value() -> u8; + fn idle_state(channel: u8, comp: bool, s: IdleState); + fn set_cms(mode: CenterAlignedMode); + } + + pub trait WithPwm: WithPwmCommon { + 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); } pub trait MasterTimer: General { type Mms; fn master_mode(&mut self, mode: Self::Mms); } + + pub trait Split { + type Channels; + fn split() -> Self::Channels; + } } -pub(crate) use sealed::{General, MasterTimer, WithPwm}; +pub(crate) use sealed::{Advanced, General, MasterTimer, WithPwm, WithPwmCommon}; pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + rcc::BusTimerClock + General { } +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, 0>,)+); + fn split() -> Self::Channels { + ($(PwmChannelDisabled::<_, $C, 0>::new(),)+) + } + } + impl Split for Rmp<$TIM, R> { + type Channels = ($(PwmChannelDisabled<$TIM, $C, R>,)+); + fn split() -> Self::Channels { + ($(PwmChannelDisabled::<_, $C, R>::new(),)+) + } + } + }; +} + macro_rules! hal { - ($($TIM:ty: [ + ($TIM:ty: [ $Timer:ident, $bits:ty, $dbg_timX_stop:ident, - $(c: ($cnum:ident $(, $aoe:ident)?),)? + $(c: ($cnum:tt $(, $aoe:ident)?),)? $(m: $timbase:ident,)? - ],)+) => { - $( - impl Instance for $TIM { } - pub type $Timer = Timer<$TIM>; - - impl General for $TIM { - type Width = $bits; - - #[inline(always)] - fn max_auto_reload() -> u32 { - <$bits>::MAX as u32 - } - #[inline(always)] - unsafe fn set_auto_reload_unchecked(&mut self, arr: u32) { - self.arr().write(|w| w.bits(arr)) - } - #[inline(always)] - fn set_auto_reload(&mut self, arr: u32) -> Result<(), Error> { - // Note: Make it impossible to set the ARR value to 0, since this - // would cause an infinite loop. - if arr > 0 && arr <= Self::max_auto_reload() { - Ok(unsafe { self.set_auto_reload_unchecked(arr) }) - } else { - Err(Error::WrongAutoReload) - } - } - #[inline(always)] - fn read_auto_reload() -> u32 { - let tim = unsafe { &*<$TIM>::ptr() }; - tim.arr().read().bits() - } - #[inline(always)] - fn enable_preload(&mut self, b: bool) { - self.cr1().modify(|_, w| w.arpe().bit(b)); - } - #[inline(always)] - fn enable_counter(&mut self) { - self.cr1().modify(|_, w| w.cen().set_bit()); - } - #[inline(always)] - fn disable_counter(&mut self) { - self.cr1().modify(|_, w| w.cen().clear_bit()); - } - #[inline(always)] - fn is_counter_enabled(&self) -> bool { - self.cr1().read().cen().is_enabled() - } - #[inline(always)] - fn reset_counter(&mut self) { - self.cnt().reset(); - } - #[inline(always)] - fn set_prescaler(&mut self, psc: u16) { - self.psc().write(|w| w.psc().set(psc) ); - } - #[inline(always)] - fn read_prescaler(&self) -> u16 { - self.psc().read().psc().bits() - } - #[inline(always)] - fn trigger_update(&mut self) { - // Sets the URS bit to prevent an interrupt from being triggered by - // the UG bit - self.cr1().modify(|_, w| w.urs().set_bit()); - self.egr().write(|w| w.ug().set_bit()); - self.cr1().modify(|_, w| w.urs().clear_bit()); - } - #[inline(always)] - fn clear_interrupt_flag(&mut self, event: Event) { - self.sr().write(|w| unsafe { w.bits(0xffff & !event.bits()) }); - } - #[inline(always)] - fn listen_interrupt(&mut self, event: Event, b: bool) { - self.dier().modify(|r, w| unsafe { w.bits( - if b { - r.bits() | event.bits() - } else { - r.bits() & !event.bits() - } - ) }); - } - #[inline(always)] - fn get_interrupt_flag(&self) -> Event { - Event::from_bits_truncate(self.sr().read().bits()) - } - #[inline(always)] - fn read_count(&self) -> Self::Width { - self.cnt().read().bits() as Self::Width - } - #[inline(always)] - fn start_one_pulse(&mut self) { - self.cr1().modify(|_, w| w.opm().set_bit().cen().set_bit()); - } - #[inline(always)] - fn cr1_reset(&mut self) { - self.cr1().reset(); - } - #[inline(always)] - fn stop_in_debug(&mut self, dbg: &mut DBG, state: bool) { - dbg.cr().modify(|_, w| w.$dbg_timX_stop().bit(state)); - } + ]) => { + impl Instance for $TIM { } + impl crate::Steal for $TIM { + unsafe fn steal() -> Self { + Self::steal() } - $(with_pwm!($TIM: $cnum $(, $aoe)?);)? - - $(impl MasterTimer for $TIM { - type Mms = pac::$timbase::cr2::MMS; - fn master_mode(&mut self, mode: Self::Mms) { - self.cr2().modify(|_,w| w.mms().variant(mode)); - } - })? - )+ - } -} + } + pub type $Timer = Timer<$TIM>; -macro_rules! with_pwm { - ($TIM:ty: CH1) => { - impl WithPwm for $TIM { - const CH_NUMBER: u8 = 1; + impl General for $TIM { + type Width = $bits; #[inline(always)] - fn read_cc_value(channel: u8) -> u32 { - let tim = unsafe { &*<$TIM>::ptr() }; - if channel < Self::CH_NUMBER { - tim.ccr(channel as usize).read().bits() + fn max_auto_reload() -> u32 { + <$bits>::MAX as u32 + } + #[inline(always)] + unsafe fn set_auto_reload_unchecked(&mut self, arr: u32) { + self.arr().write(|w| w.bits(arr)) + } + #[inline(always)] + fn set_auto_reload(&mut self, arr: u32) -> Result<(), Error> { + // Note: Make it impossible to set the ARR value to 0, since this + // would cause an infinite loop. + if arr > 0 && arr <= Self::max_auto_reload() { + Ok(unsafe { self.set_auto_reload_unchecked(arr) }) } else { - 0 + Err(Error::WrongAutoReload) } } - #[inline(always)] - fn set_cc_value(channel: u8, value: u32) { + fn read_auto_reload() -> u32 { let tim = unsafe { &*<$TIM>::ptr() }; - #[allow(unused_unsafe)] - if channel < Self::CH_NUMBER { - tim.ccr(channel as usize).write(|w| unsafe { w.bits(value) }) - } + tim.arr().read().bits() } - #[inline(always)] - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm) { - match channel { - Channel::C1 => { - self.ccmr1_output() - .modify(|_, w| w.oc1pe().set_bit().oc1m().set(mode as _) ); - } - _ => {}, - } + fn enable_preload(&mut self, b: bool) { + self.cr1().modify(|_, w| w.arpe().bit(b)); } - #[inline(always)] - fn start_pwm(&mut self) { - self.cr1().modify(|_, w| w.cen().set_bit()); + fn enable_counter(&mut self, b: bool) { + self.cr1().modify(|_, w| w.cen().bit(b)); } - #[inline(always)] - fn enable_channel(c: u8, b: bool) { - let tim = unsafe { &*<$TIM>::ptr() }; - if c < Self::CH_NUMBER { - unsafe { bb::write(tim.ccer(), c*4, b); } - } + fn is_counter_enabled(&self) -> bool { + self.cr1().read().cen().is_enabled() } - } - }; - ($TIM:ty: CH2) => { - impl WithPwm for $TIM { - const CH_NUMBER: u8 = 2; - #[inline(always)] - fn read_cc_value(channel: u8) -> u32 { - let tim = unsafe { &*<$TIM>::ptr() }; - if channel < Self::CH_NUMBER { - tim.ccr(channel as usize).read().bits() - } else { - 0 - } + fn reset_counter(&mut self) { + self.cnt().reset(); } - #[inline(always)] - fn set_cc_value(channel: u8, value: u32) { - let tim = unsafe { &*<$TIM>::ptr() }; - #[allow(unused_unsafe)] - if channel < Self::CH_NUMBER { - tim.ccr(channel as usize).write(|w| unsafe { w.bits(value) }) - } + fn set_prescaler(&mut self, psc: u16) { + self.psc().write(|w| w.psc().set(psc) ); } - #[inline(always)] - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm) { - match channel { - Channel::C1 => { - self.ccmr1_output() - .modify(|_, w| w.oc1pe().set_bit().oc1m().set(mode as _) ); - } - Channel::C2 => { - self.ccmr1_output() - .modify(|_, w| w.oc2pe().set_bit().oc2m().set(mode as _) ); + fn read_prescaler(&self) -> u16 { + self.psc().read().psc().bits() + } + #[inline(always)] + fn trigger_update(&mut self) { + // Sets the URS bit to prevent an interrupt from being triggered by + // the UG bit + self.cr1().modify(|_, w| w.urs().set_bit()); + self.egr().write(|w| w.ug().set_bit()); + self.cr1().modify(|_, w| w.urs().clear_bit()); + } + #[inline(always)] + fn clear_interrupt_flag(&mut self, event: Event) { + self.sr().write(|w| unsafe { w.bits(0xffff & !event.bits()) }); + } + #[inline(always)] + fn listen_interrupt(&mut self, event: Event, b: bool) { + self.dier().modify(|r, w| unsafe { w.bits( + if b { + r.bits() | event.bits() + } else { + r.bits() & !event.bits() } - _ => {}, - } + ) }); } - #[inline(always)] - fn start_pwm(&mut self) { - self.cr1().modify(|_, w| w.cen().set_bit()); + fn get_interrupt_flag(&self) -> Event { + Event::from_bits_truncate(self.sr().read().bits()) } - #[inline(always)] - fn enable_channel(c: u8, b: bool) { - let tim = unsafe { &*<$TIM>::ptr() }; - if c < Self::CH_NUMBER { - unsafe { bb::write(tim.ccer(), c*4, b); } - } + fn read_count(&self) -> Self::Width { + self.cnt().read().bits() as Self::Width } - } - }; - ($TIM:ty: CH4 $(, $aoe:ident)?) => { - impl WithPwm for $TIM { - const CH_NUMBER: u8 = 4; - #[inline(always)] - fn read_cc_value(channel: u8) -> u32 { - let tim = unsafe { &*<$TIM>::ptr() }; - tim.ccr(channel as usize).read().bits() + fn start_one_pulse(&mut self) { + self.cr1().modify(|_, w| w.opm().set_bit().cen().set_bit()); } - #[inline(always)] - fn set_cc_value(channel: u8, value: u32) { - let tim = unsafe { &*<$TIM>::ptr() }; - tim.ccr(channel as usize).write(|w| unsafe { w.bits(value) }) + fn cr1_reset(&mut self) { + self.cr1().reset(); + } + #[inline(always)] + fn cnt_reset(&mut self) { + self.cnt().reset(); } - #[inline(always)] - fn preload_output_channel_in_mode(&mut self, channel: Channel, mode: Ocm) { - match channel { - Channel::C1 => { - self.ccmr1_output() - .modify(|_, w| w.oc1pe().set_bit().oc1m().set(mode as _) ); + fn stop_in_debug(&mut self, dbg: &mut DBG, state: bool) { + dbg.cr().modify(|_, w| w.$dbg_timX_stop().bit(state)); + } + } + $( + impl WithPwmCommon for $TIM { + const CH_NUMBER: u8 = $cnum; + const COMP_CH_NUMBER: u8 = $cnum; + + #[inline(always)] + fn read_cc_value(c: u8) -> u32 { + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::CH_NUMBER { + tim.ccr(c as usize).read().bits() + } else { + 0 + } + } + + #[inline(always)] + fn set_cc_value(c: u8, value: u32) { + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::CH_NUMBER { + tim.ccr(c as usize).write(|w| unsafe { w.bits(value) }) } - Channel::C2 => { - self.ccmr1_output() - .modify(|_, w| w.oc2pe().set_bit().oc2m().set(mode as _) ); + } + + #[inline(always)] + fn enable_channel(c: u8, b: bool) { + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::CH_NUMBER { + unsafe { bb::write(tim.ccer(), c*4, b); } } - Channel::C3 => { - self.ccmr2_output() - .modify(|_, w| w.oc3pe().set_bit().oc3m().set(mode as _) ); + } + + #[inline(always)] + fn set_channel_polarity(c: u8, p: Polarity) { + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::CH_NUMBER { + unsafe { bb::write(tim.ccer(), c*4 + 1, p == Polarity::ActiveLow); } } - Channel::C4 => { - self.ccmr2_output() - .modify(|_, w| w.oc4pe().set_bit().oc4m().set(mode as _) ); + } + + #[inline(always)] + fn set_nchannel_polarity(c: u8, p: Polarity) { + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::COMP_CH_NUMBER { + unsafe { bb::write(tim.ccer(), c*4 + 3, p == Polarity::ActiveLow); } } } } - #[inline(always)] - fn start_pwm(&mut self) { - $(let $aoe = self.bdtr().modify(|_, w| w.aoe().set_bit());)? - self.cr1().modify(|_, w| w.cen().set_bit()); + $( + impl Advanced for $TIM { + fn enable_nchannel(c: u8, b: bool) { + let $aoe = (); + let tim = unsafe { &*<$TIM>::ptr() }; + if c < Self::COMP_CH_NUMBER { + unsafe { bb::write(tim.ccer(), c*4 + 2, b); } + } + } + fn set_dtg_value(value: u8) { + let tim = unsafe { &*<$TIM>::ptr() }; + tim.bdtr().modify(|_,w| w.dtg().set(value)); + } + fn read_dtg_value() -> u8 { + let tim = unsafe { &*<$TIM>::ptr() }; + tim.bdtr().read().dtg().bits() + } + fn idle_state(c: u8, comp: bool, s: IdleState) { + let tim = unsafe { &*<$TIM>::ptr() }; + if !comp { + if c < Self::CH_NUMBER { + unsafe { bb::write(tim.cr2(), c*2 + 8, s == IdleState::Set); } + } + } else { + if c < Self::COMP_CH_NUMBER { + unsafe { bb::write(tim.cr2(), c*2 + 9, s == IdleState::Set); } + } + } + } + #[inline(always)] + fn set_cms(cms: CenterAlignedMode) { + let tim = unsafe { &*<$TIM>::ptr() }; + tim.cr1().write(|w| w.cms().variant(cms)); + } + } + )? + + with_pwm!($TIM: $cnum $(, $aoe)?); + split!($TIM: $cnum); + )? + + $(impl MasterTimer for $TIM { + type Mms = pac::$timbase::cr2::MMS; + fn master_mode(&mut self, mode: Self::Mms) { + self.cr2().modify(|_,w| w.mms().variant(mode)); } + })? + } +} +macro_rules! with_pwm { + ($TIM:ty: [$($Cx:literal, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident;)+] $(, $aoe:ident)?) => { + impl WithPwm for $TIM { #[inline(always)] - fn enable_channel(c: u8, b: bool) { - let tim = unsafe { &*<$TIM>::ptr() }; - if c < Self::CH_NUMBER { - unsafe { bb::write(tim.ccer(), c*4, b); } + fn preload_output_channel_in_mode(&mut self, c: u8, mode: Ocm) { + match c { + $( + $Cx => { + self.$ccmrx_output() + .modify(|_, w| w.$ocxpe().set_bit().$ocxm().set(mode as _) ); + } + )+ + #[allow(unreachable_patterns)] + _ => {}, } } + 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) { + $(let $aoe = self.bdtr().modify(|_, w| w.aoe().set_bit());)? + self.cr1().modify(|_, w| w.cen().set_bit()); + } } - } + }; + ($TIM:ty: 1) => { + with_pwm!($TIM: [ + 0, ccmr1_output, oc1pe, oc1m; + ]); + }; + ($TIM:ty: 2) => { + with_pwm!($TIM: [ + 0, ccmr1_output, oc1pe, oc1m; + 1, ccmr1_output, oc2pe, oc2m; + ]); + }; + ($TIM:ty: 4 $(, $aoe:ident)?) => { + with_pwm!($TIM: [ + 0, ccmr1_output, oc1pe, oc1m; + 1, ccmr1_output, oc2pe, oc2m; + 2, ccmr2_output, oc3pe, oc3m; + 3, ccmr2_output, oc4pe, oc4m; + ] $(, $aoe)?); + }; } impl Timer { @@ -746,50 +809,36 @@ const fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u32) { (psc as u16, arr) } -hal!( - pac::TIM2: [Timer2, u16, dbg_tim2_stop, c: (CH4), m: tim2,], - pac::TIM3: [Timer3, u16, dbg_tim3_stop, c: (CH4), m: tim2,], -); - #[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -hal!( - pac::TIM1: [Timer1, u16, dbg_tim1_stop, c: (CH4, _aoe), m: tim1,], -); +hal!(pac::TIM1: [Timer1, u16, dbg_tim1_stop, c: (4, _aoe), m: tim1,]); + +hal!(pac::TIM2: [Timer2, u16, dbg_tim2_stop, c: (4), m: tim2,]); +hal!(pac::TIM3: [Timer3, u16, dbg_tim3_stop, c: (4), m: tim2,]); + +#[cfg(feature = "medium")] +hal!(pac::TIM4: [Timer4, u16, dbg_tim4_stop, c: (4), m: tim2,]); + +#[cfg(any(feature = "high", feature = "connectivity"))] +hal!(pac::TIM5: [Timer5, u16, dbg_tim5_stop, c: (4), m: tim2,]); #[cfg(any(feature = "stm32f100", feature = "high", feature = "connectivity"))] -hal! { - pac::TIM6: [Timer6, u16, dbg_tim6_stop, m: tim6,], -} +hal!(pac::TIM6: [Timer6, u16, dbg_tim6_stop, m: tim6,]); #[cfg(any( all(feature = "high", any(feature = "stm32f101", feature = "stm32f103")), any(feature = "stm32f100", feature = "connectivity") ))] -hal! { - pac::TIM7: [Timer7, u16, dbg_tim7_stop, m: tim6,], -} - -#[cfg(feature = "stm32f100")] -hal! { - pac::TIM15: [Timer15, u16, dbg_tim15_stop, c: (CH2),], - pac::TIM16: [Timer16, u16, dbg_tim16_stop, c: (CH1),], - pac::TIM17: [Timer17, u16, dbg_tim17_stop, c: (CH1),], -} - -#[cfg(feature = "medium")] -hal! { - pac::TIM4: [Timer4, u16, dbg_tim4_stop, c: (CH4), m: tim2,], -} - -#[cfg(any(feature = "high", feature = "connectivity"))] -hal! { - pac::TIM5: [Timer5, u16, dbg_tim5_stop, c: (CH4), m: tim2,], -} +hal!(pac::TIM7: [Timer7, u16, dbg_tim7_stop, m: tim6,]); #[cfg(all(feature = "stm32f103", feature = "high"))] -hal! { - pac::TIM8: [Timer8, u16, dbg_tim8_stop, c: (CH4, _aoe), m: tim1,], -} +hal!(pac::TIM8: [Timer8, u16, dbg_tim8_stop, c: (4, _aoe), m: tim1,]); + +#[cfg(feature = "stm32f100")] +hal!(pac::TIM15: [Timer15, u16, dbg_tim15_stop, c: (2),]); +#[cfg(feature = "stm32f100")] +hal!(pac::TIM16: [Timer16, u16, dbg_tim16_stop, c: (1),]); +#[cfg(feature = "stm32f100")] +hal!(pac::TIM17: [Timer17, u16, dbg_tim17_stop, c: (1),]); //TODO: restore these timers once stm32-rs has been updated /* diff --git a/src/timer/counter.rs b/src/timer/counter.rs index 2f811434..8fa59572 100644 --- a/src/timer/counter.rs +++ b/src/timer/counter.rs @@ -32,7 +32,7 @@ impl CounterHz { impl CounterHz { pub fn start(&mut self, timeout: Hertz) -> Result<(), Error> { // pause - self.tim.disable_counter(); + self.tim.enable_counter(false); self.tim.clear_interrupt_flag(Event::Update); @@ -47,7 +47,7 @@ impl CounterHz { self.tim.trigger_update(); // start counter - self.tim.enable_counter(); + self.tim.enable_counter(true); Ok(()) } @@ -67,14 +67,14 @@ impl CounterHz { } // disable counter - self.tim.disable_counter(); + self.tim.enable_counter(false); Ok(()) } /// Restarts the timer in count down mode with user-defined prescaler and auto-reload register pub fn start_raw(&mut self, psc: u16, arr: u16) { // pause - self.tim.disable_counter(); + self.tim.enable_counter(false); self.tim.set_prescaler(psc); @@ -84,7 +84,7 @@ impl CounterHz { self.tim.trigger_update(); // start counter - self.tim.enable_counter(); + self.tim.enable_counter(true); } /// Retrieves the content of the prescaler register. The real prescaler is this value + 1. @@ -160,7 +160,7 @@ impl Counter { pub fn start(&mut self, timeout: TimerDurationU32) -> Result<(), Error> { // pause - self.tim.disable_counter(); + self.tim.enable_counter(false); self.tim.clear_interrupt_flag(Event::Update); @@ -173,7 +173,7 @@ impl Counter { self.tim.trigger_update(); // start counter - self.tim.enable_counter(); + self.tim.enable_counter(true); Ok(()) } @@ -193,7 +193,7 @@ impl Counter { } // disable counter - self.tim.disable_counter(); + self.tim.enable_counter(false); Ok(()) } } diff --git a/src/timer/hal_02.rs b/src/timer/hal_02.rs index 42c4d26d..89942085 100644 --- a/src/timer/hal_02.rs +++ b/src/timer/hal_02.rs @@ -11,8 +11,8 @@ use fugit::{ExtU32, HertzU32 as Hertz, TimerDurationU32}; use void::Void; use super::{ - pins::sealed::Remap, pwm::Pins, Channel, Counter, CounterHz, Delay, Error, Instance, Pwm, - PwmChannel, PwmHz, SysCounter, SysCounterHz, SysDelay, WithPwm, + Counter, CounterHz, Delay, ErasedChannel, Error, Instance, PwmChannel, SysCounter, + SysCounterHz, SysDelay, TimC, WithPwm, }; impl DelayUs for SysDelay { @@ -133,7 +133,9 @@ impl Cancel for SysCounter { } } -impl embedded_hal_02::PwmPin for PwmChannel { +impl, const C: u8, const COMP: bool> embedded_hal_02::PwmPin + for PwmChannel +{ type Duty = u16; fn disable(&mut self) { @@ -153,46 +155,23 @@ impl embedded_hal_02::PwmPin for PwmChanne } } -impl embedded_hal_02::Pwm for PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - 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) } } @@ -263,46 +242,3 @@ impl Cancel for Counter { self.cancel() } } - -impl embedded_hal_02::Pwm for Pwm -where - TIM: Instance + WithPwm, - REMAP: Remap, - 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..c7444ea9 100644 --- a/src/timer/hal_1.rs +++ b/src/timer/hal_1.rs @@ -1,12 +1,7 @@ -//! Delay implementation based on general-purpose 32 bit timers and System timer (SysTick). -//! -//! TIM2 and TIM5 are a general purpose 32-bit auto-reload up/downcounter with -//! a 16-bit prescaler. - use core::convert::Infallible; use embedded_hal::delay::DelayNs; -use super::{Delay, Instance, PwmChannel, SysDelay, WithPwm}; +use super::{Delay, ErasedChannel, Instance, PwmChannel, SysDelay, TimC, WithPwm}; use fugit::ExtU32Ceil; impl DelayNs for SysDelay { @@ -33,11 +28,29 @@ impl DelayNs for Delay { } } -impl embedded_hal::pwm::ErrorType for PwmChannel { +impl, const C: u8, const COMP: bool> embedded_hal::pwm::ErrorType + for PwmChannel +{ + type Error = Infallible; +} + +impl, const C: u8, const COMP: bool> + 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 PwmChannel { +impl embedded_hal::pwm::SetDutyCycle for ErasedChannel { fn max_duty_cycle(&self) -> u16 { self.get_max_duty() } diff --git a/src/timer/pins.rs b/src/timer/pins.rs deleted file mode 100644 index e0e9c716..00000000 --- a/src/timer/pins.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::pac; - -pub trait CPin {} -pub struct Ch; -pub const C1: u8 = 0; -pub const C2: u8 = 1; -pub const C3: u8 = 2; -pub const C4: u8 = 3; - -pub(crate) mod sealed { - pub trait Remap { - type Periph; - const REMAP: u8; - - fn remap(mapr: &mut crate::afio::MAPR); - } -} - -macro_rules! remap { - ($($name:ident: ($TIMX:ty, $state:literal, $P1:ident, $P2:ident, $P3:ident, $P4:ident, { $remapex:expr }),)+) => { - $( - pub struct $name; - impl sealed::Remap for $name { - type Periph = $TIMX; - const REMAP: u8 = $state; - - fn remap(mapr: &mut crate::afio::MAPR) { - mapr.modify_mapr($remapex); - } - } - impl CPin<$name, 0> for crate::gpio::$P1 {} - impl CPin<$name, 1> for crate::gpio::$P2 {} - impl CPin<$name, 2> for crate::gpio::$P3 {} - impl CPin<$name, 3> for crate::gpio::$P4 {} - )+ - } -} - -#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -remap!( - Tim1NoRemap: (pac::TIM1, 0b00, PA8, PA9, PA10, PA11, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), - //Tim1PartialRemap: (pac::TIM1, 0b01, PA8, PA9, PA10, PA11), - Tim1FullRemap: (pac::TIM1, 0b11, PE9, PE11, PE13, PE14, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), -); - -remap!( - Tim2NoRemap: (pac::TIM2, 0b00, PA0, PA1, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2PartialRemap1: (pac::TIM2, 0b01, PA15, PB3, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2PartialRemap2: (pac::TIM2, 0b10, PA0, PA1, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2FullRemap: (pac::TIM2, 0b11, PA15, PB3, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - - Tim3NoRemap: (pac::TIM3, 0b00, PA6, PA7, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), - Tim3PartialRemap: (pac::TIM3, 0b10, PB4, PB5, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), - Tim3FullRemap: (pac::TIM3, 0b11, PC6, PC7, PC8, PC9, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), -); - -#[cfg(feature = "medium")] -remap!( - Tim4NoRemap: (pac::TIM4, 0b00, PB6, PB7, PB8, PB9, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), - Tim4Remap: (pac::TIM4, 0b01, PD12, PD13, PD14, PD15, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), -); diff --git a/src/timer/pwm.rs b/src/timer/pwm.rs index 01fe1e61..5d1a2a93 100644 --- a/src/timer/pwm.rs +++ b/src/timer/pwm.rs @@ -1,421 +1,353 @@ -/*! - # Pulse width modulation - - The general purpose timers (`TIM2`, `TIM3`, and `TIM4`) can be used to output - pulse width modulated signals on some pins. The timers support up to 4 - simultaneous pwm outputs in separate `Channels` - - ## Usage for pre-defined channel combinations - - This crate only defines basic channel combinations for default AFIO remappings, - where all the channels are enabled. Start by setting all the pins for the - timer you want to use to alternate push pull pins: - - ```rust - let gpioa = ..; // Set up and split GPIOA - // Select the pins you want to use - let pins = ( - gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl), - ); - - // Set up the timer as a PWM output. If selected pins may correspond to different remap options, - // then you must specify the remap generic parameter. Otherwise, if there is no such ambiguity, - // the remap generic parameter can be omitted without complains from the compiler. - let (c1, c2, c3, c4) = Timer::tim2(p.TIM2, &clocks) - .pwm_hz::(pins, &mut afio.mapr, 1.kHz()) - .3; - - // Start using the channels - c1.set_duty(c1.get_max_duty()); - // ... - ``` - - Then call the `pwm` function on the corresponding timer. - - NOTE: In some cases you need to specify remap you need, especially for TIM2 - (see [Alternate function remapping](super)): - - ``` - let device: pac::Peripherals = ..; - - // Put the timer in PWM mode using the specified pins - // with a frequency of 100 Hz. - let (c0, c1, c2, c3) = Timer::tim2(device.TIM2, &clocks) - .pwm_hz::(pins, &mut afio.mapr, 100.Hz()); - - // Set the duty cycle of channel 0 to 50% - c0.set_duty(c0.get_max_duty() / 2); - // PWM outputs are disabled by default - c0.enable() - ``` -*/ - -use crate::afio::MAPR; -use crate::gpio::{self, Alternate}; - -use super::{compute_arr_presc, Channel, FTimer, Instance, Ocm, Timer, WithPwm}; +//! Provides basic Pulse-width modulation (PWM) capabilities +//! +//! There are 2 main structures [`Pwm`] and [`PwmHz`]. Both structures implement [`embedded_hal_02::Pwm`] and have some additional API. +//! +//! First one is based on [`FTimer`] with fixed prescaler +//! and easy to use with [`fugit::TimerDurationU32`] for setting pulse width and period without advanced calculations. +//! +//! Second one is based on [`Timer`] with dynamic internally calculated prescaler and require [`fugit::Hertz`] to set 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. +//! +//! ```rust,ignore +//! let (pwm_manager, (pwm_ch1, pwm_ch2, ..)) = dp.TIM1.pwm_us(100.micros(), &clocks); +//! ``` +//! +//! 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 mut pwm_c1 = pwm_c1.with(gpioa.pa8).with_complementary(gpioa.pa7); +//! pwm_c1.enable(); +//! pwm_c1.enable_complementary(); +//! ``` +//! +//! 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. +//! +//! `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, CenterAlignedMode, FTimer, IdleState, Instance, Ocm, Polarity, + TimC, TimNC, Timer, WithPwm, +}; +pub use super::{Ch, C1, C2, C3, C4}; +use crate::afio::{RInto, Rmp}; use crate::rcc::Clocks; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use fugit::{HertzU32 as Hertz, TimerDurationU32}; -pub trait Pins { - const C1: bool = false; - const C2: bool = false; - const C3: bool = false; - const C4: bool = false; - type Channels; - - 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 split() -> Self::Channels; -} - -pub use super::{pins::sealed::Remap, CPin, Ch, C1, C2, C3, C4}; - -pub struct PwmChannel { - pub(super) _tim: PhantomData, -} - -macro_rules! pins_impl { - ( $( ( $($PINX:ident),+ ), ( $($ENCHX:ident),+ ); )+ ) => { - $( - #[allow(unused_parens)] - impl Pins),+)> for ($($PINX),+) - where - TIM: Instance + WithPwm, - REMAP: Remap, - $($PINX: CPin + gpio::PinExt>,)+ - { - $(const $ENCHX: bool = true;)+ - type Channels = ($(PwmChannel),+); - fn split() -> Self::Channels { - ($(PwmChannel::::new()),+) - } - } - )+ - }; -} - -pins_impl!( - (P1, P2, P3, P4), (C1, C2, C3, C4); - (P2, P3, P4), (C2, C3, C4); - (P1, P3, P4), (C1, C3, C4); - (P1, P2, P4), (C1, C2, C4); - (P1, P2, P3), (C1, C2, C3); - (P3, P4), (C3, C4); - (P2, P4), (C2, C4); - (P2, P3), (C2, C3); - (P1, P4), (C1, C4); - (P1, P3), (C1, C3); - (P1, P2), (C1, C2); - (P1), (C1); - (P2), (C2); - (P3), (C3); - (P4), (C4); -); - pub trait PwmExt where - Self: Sized + Instance + WithPwm, + Self: Sized + Instance + WithPwm + Split, { - fn pwm( + fn pwm( self, - pins: PINS, - mapr: &mut MAPR, time: TimerDurationU32, clocks: &Clocks, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins; + ) -> (PwmManager, Self::Channels); - fn pwm_hz( - self, - pins: PINS, - mapr: &mut MAPR, - freq: Hertz, - clocks: &Clocks, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins; + fn pwm_hz(self, freq: Hertz, clocks: &Clocks) -> (PwmHzManager, Self::Channels); - fn pwm_us( + fn pwm_us( self, - pins: PINS, - mapr: &mut MAPR, time: TimerDurationU32<1_000_000>, clocks: &Clocks, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - self.pwm::<_, _, _, 1_000_000>(pins, mapr, 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, - mapr: &mut MAPR, time: TimerDurationU32, clocks: &Clocks, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - FTimer::::new(self, clocks).pwm(pins, mapr, time) + ) -> (PwmManager, Self::Channels) { + FTimer::::new(self, clocks).pwm(time) } - fn pwm_hz( - self, - pins: PINS, - mapr: &mut MAPR, - time: Hertz, - clocks: &Clocks, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins, - { - Timer::new(self, clocks).pwm_hz(pins, mapr, time) + fn pwm_hz(self, freq: Hertz, clocks: &Clocks) -> (PwmHzManager, Self::Channels) { + Timer::new(self, clocks).pwm_hz(freq) } } -impl PwmChannel { - pub(crate) fn new() -> Self { - Self { - _tim: core::marker::PhantomData, - } +impl Rmp +where + TIM: Sized + Instance + WithPwm, + Self: Split, +{ + pub fn pwm( + self, + time: TimerDurationU32, + clocks: &Clocks, + ) -> (PwmManager, ::Channels) { + let mut timer = FTimer::::new(self.0, clocks); + timer._pwm_init(time); + (PwmManager { timer }, Self::split()) } -} -impl PwmChannel { - #[inline] - pub fn disable(&mut self) { - TIM::enable_channel(C, false); + pub fn pwm_us( + self, + time: TimerDurationU32<1_000_000>, + clocks: &Clocks, + ) -> (PwmManager, ::Channels) { + self.pwm::<1_000_000>(time, clocks) } - #[inline] - pub fn enable(&mut self) { - TIM::enable_channel(C, true); + pub fn pwm_hz(self, freq: Hertz, clocks: &Clocks) -> (PwmHzManager, ::Channels) { + let mut timer = Timer::new(self.0, clocks); + timer._pwm_hz_init(freq); + (PwmHzManager { timer }, Self::split()) } +} - #[inline] - pub fn get_duty(&self) -> u16 { - TIM::read_cc_value(C) as u16 +impl Timer { + pub fn pwm_hz(mut self, freq: Hertz) -> (PwmHzManager, TIM::Channels) { + self._pwm_hz_init(freq); + (PwmHzManager { timer: self }, TIM::split()) } +} +impl Timer { + fn _pwm_hz_init(&mut self, freq: Hertz) { + // 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); - /// 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) - } + 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(); - #[inline] - pub fn set_duty(&mut self, duty: u16) { - TIM::set_cc_value(C, duty as u32) + self.tim.start_pwm(); } } -pub struct PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - timer: Timer, - _pins: PhantomData<(REMAP, P, PINS)>, +impl FTimer { + pub fn pwm(mut self, time: TimerDurationU32) -> (PwmManager, TIM::Channels) { + self._pwm_init(time); + (PwmManager { timer: self }, TIM::split()) + } } +impl FTimer { + fn _pwm_init(&mut self, time: TimerDurationU32) { + // 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); -impl PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - pub fn release(mut self) -> Timer { - // stop timer - self.tim.cr1_reset(); - self.timer - } + self.tim.set_auto_reload(time.ticks() - 1).unwrap(); + + // Trigger update event to load the registers + self.tim.trigger_update(); - pub fn split(self) -> PINS::Channels { - PINS::split() + self.tim.start_pwm(); } } -impl Deref for PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - type Target = Timer; - fn deref(&self) -> &Self::Target { - &self.timer +pub struct PwmChannelDisabled { + pub(super) tim: TIM, +} + +impl PwmChannelDisabled { + pub(crate) fn new() -> Self { + Self { + tim: unsafe { TIM::steal() }, + } } } -impl DerefMut for PwmHz +impl PwmChannelDisabled where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, + TIM: TimC, { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.timer + pub fn with(mut self, pin: impl RInto) -> PwmChannel { + self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1); + PwmChannel { + tim: self.tim, + regular: pin.rinto(), + } } } -impl Timer { - pub fn pwm_hz( - mut self, - _pins: PINS, - mapr: &mut MAPR, - freq: Hertz, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - - if PINS::C1 { - self.tim - .preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1); +pub struct PwmChannel, const C: u8, const COMP: bool = false> { + pub(super) tim: TIM, + regular: TIM::Out, + // TODO: add complementary pins +} + +impl, const C: u8, const COMP: bool> PwmChannel { + pub const fn channel(&self) -> u8 { + C + } + /*pub fn release(mut self) -> (PwmChannelDisabled, TIM::Out) { + self.tim.freeze_output_channel(C); + (PwmChannelDisabled { tim: self.tim }, self.regular) + }*/ + pub fn erase(self) -> ErasedChannel { + ErasedChannel { + _tim: self.tim, + channel: C, } - if PINS::C2 && TIM::CH_NUMBER > 1 { - self.tim - .preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1); + } +} +impl, const C: u8, const COMP: bool> PwmChannel { + pub fn with(self, pin: impl Into) -> Self { + Self { + tim: self.tim, + regular: pin.into(), } - if PINS::C3 && TIM::CH_NUMBER > 2 { - self.tim - .preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1); + } +} +impl + TimNC, const C: u8, const COMP: bool> PwmChannel { + pub fn with_complementary(self, pin: impl Into) -> PwmChannel { + let _pin = pin.into(); + PwmChannel { + tim: self.tim, + regular: self.regular, + } + } +} + +pub struct ErasedChannel { + _tim: TIM, + channel: u8, +} + +impl ErasedChannel { + pub const fn channel(&self) -> u8 { + self.channel + } +} + +macro_rules! ch_impl { + () => { + /// Disable PWM channel + #[inline] + pub fn disable(&mut self) { + TIM::enable_channel(self.channel(), false); } - if PINS::C4 && TIM::CH_NUMBER > 3 { - self.tim - .preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1); + + /// Enable PWM channel + #[inline] + pub fn enable(&mut self) { + TIM::enable_channel(self.channel(), true); } - // 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(self.channel()) 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(self.channel(), duty as u32) + } - self.tim.start_pwm(); + /// Set PWM channel polarity + #[inline] + pub fn set_polarity(&mut self, p: Polarity) { + TIM::set_channel_polarity(self.channel(), p); + } - PwmHz { - timer: self, - _pins: PhantomData, + /// 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, - REMAP: Remap, - PINS: Pins, -{ - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) - } - - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) - } +macro_rules! chN_impl { + () => { + /// Disable complementary PWM channel + #[inline] + pub fn disable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), false); + } - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 - } + /// Enable complementary PWM channel + #[inline] + pub fn enable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), true); + } - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty as u32) - } + /// Set PWM channel idle state + #[inline] + pub fn set_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), false, s); + } - /// 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) - } + /// Set complementary PWM channel idle state + #[inline] + pub fn set_complementary_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), true, s); + } + }; +} - pub fn get_period(&self) -> Hertz { - let clk = self.clk; - let psc = self.tim.read_prescaler() as u32; - let arr = TIM::read_auto_reload(); +impl, const C: u8, const COMP: bool> PwmChannel { + ch_impl!(); +} - // Length in ms of an internal clock pulse - clk / ((psc + 1) * (arr + 1)) - } +impl, const C: u8> PwmChannel { + chN_impl!(); +} - pub fn set_period(&mut self, period: Hertz) { - let clk = self.clk; +impl ErasedChannel { + ch_impl!(); +} - let (psc, arr) = compute_arr_presc(period.raw(), clk.raw()); - self.tim.set_prescaler(psc); - self.tim.set_auto_reload(arr).unwrap(); - } +impl ErasedChannel { + chN_impl!(); } -pub struct Pwm +pub struct PwmManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { - timer: FTimer, - _pins: PhantomData<(REMAP, P, PINS)>, + pub(super) timer: FTimer, } -impl Pwm +impl PwmManager where - TIM: Instance + WithPwm, - REMAP: Remap, - 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, - REMAP: Remap, - PINS: Pins, { type Target = FTimer; fn deref(&self) -> &Self::Target { @@ -423,98 +355,186 @@ where } } -impl DerefMut for Pwm +impl DerefMut for PwmManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.timer } } -impl FTimer { - pub fn pwm( - mut self, - _pins: PINS, - mapr: &mut MAPR, - time: TimerDurationU32, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - - 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(); +pub struct PwmHzManager +where + TIM: Instance + WithPwm, +{ + pub(super) timer: Timer, +} - self.tim.start_pwm(); +/*impl PwmHzManager +where + TIM: Instance + WithPwm + Split, +{ + pub fn release(mut self, _channels: TIM::Channels) -> Timer { + // stop timer + self.tim.cr1_reset(); + self.timer + } +}*/ - Pwm { - timer: self, - _pins: PhantomData, - } +impl Deref for PwmHzManager +where + TIM: Instance + WithPwm, +{ + type Target = Timer; + fn deref(&self) -> &Self::Target { + &self.timer } } -impl Pwm +impl DerefMut for PwmHzManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.timer } +} - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) +impl PwmManager +where + TIM: Instance + WithPwm, +{ + /// 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) } - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 + /// Get the PWM frequency of the timer as a duration + pub fn get_period(&self) -> TimerDurationU32 { + TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1) } - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into()) + /// Set the PWM frequency for the timer from a duration + pub fn set_period(&mut self, period: TimerDurationU32) { + self.tim.set_auto_reload(period.ticks() - 1).unwrap(); + self.tim.cnt_reset(); } +} +impl PwmHzManager +where + TIM: Instance + WithPwm, +{ + /// 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) } - pub fn get_period(&self) -> TimerDurationU32 { - TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1) + /// 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(); + + // Length in ms of an internal clock pulse + clk / ((psc + 1) * (arr + 1)) } - pub fn set_period(&mut self, period: TimerDurationU32) { - self.tim.set_auto_reload(period.ticks() - 1).unwrap(); + /// Set the PWM frequency for the timer in Hertz + pub fn set_period(&mut self, period: Hertz) { + let clk = self.clk; + + 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(); + } +} + +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 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); + } + + /// 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() + } + + /// Sets the alignment mode + #[inline] + pub fn set_cms(&mut self, mode: CenterAlignedMode) { + self.tim.enable_counter(false); + TIM::set_cms(mode); + self.tim.enable_counter(true); + } + }; +} + +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. +/// Values greater than 1009 result in maximum dead time of 126 us +const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 { + match dts_ticks { + 0..=127 => dts_ticks as u8, + 128..=254 => ((((dts_ticks + 1) >> 1) - 64) as u8) | 0b_1000_0000, + 255..=504 => ((((dts_ticks + 7) >> 3) - 32) as u8) | 0b_1100_0000, + 505..=1008 => ((((dts_ticks + 15) >> 4) - 32) as u8) | 0b_1110_0000, + 1009.. => 0xff, + } +} + +/// Convert raw DTG register bits value to number of dead time ticks +const fn unpack_dead_time(bits: u8) -> u16 { + if bits & 0b_1000_0000 == 0 { + bits as u16 + } else if bits & 0b_0100_0000 == 0 { + (((bits & !0b_1000_0000) as u16) + 64) * 2 + } else if bits & 0b_0010_0000 == 0 { + (((bits & !0b_1100_0000) as u16) + 32) * 8 + } else { + (((bits & !0b_1110_0000) as u16) + 32) * 16 } }