diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 8357b6cd..b62a1466 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,22 +1,18 @@ -use crate::gpio; -use crate::rcc::RccPeripheral; -use crate::time::Hertz; -use core::marker::PhantomData; -use embassy::util::Unborrow; -use embassy_hal_common::unborrow; -use stm32_metapac::timer::vals::Ocm; +#[cfg(feature = "unstable-pac")] +#[macro_use] +pub mod pins; -pub struct Pwm<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +#[cfg(not(feature = "unstable-pac"))] +#[macro_use] +pub(crate) mod pins; + +pub mod simple_pwm; + +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; } -// TIM2 - -pub struct Ch1 {} -pub struct Ch2 {} -pub struct Ch3 {} -pub struct Ch4 {} - #[derive(Clone, Copy)] pub enum Channel { Ch1, @@ -25,171 +21,241 @@ pub enum Channel { Ch4, } -impl<'d, T: Instance> Pwm<'d, T> { - pub fn new>( - _tim: impl Unborrow + 'd, - ch1: impl Unborrow> + 'd, - ch2: impl Unborrow> + 'd, - ch3: impl Unborrow> + 'd, - ch4: impl Unborrow> + 'd, - freq: F, - ) -> Self { - unborrow!(ch1, ch2, ch3, ch4); - - T::enable(); - T::reset(); - let r = T::regs(); - - let mut this = Pwm { - phantom: PhantomData, - }; - unsafe { - ch1.configure(); - ch2.configure(); - ch3.configure(); - ch4.configure(); - } - - unsafe { - use stm32_metapac::timer::vals::Dir; - this.set_freq(freq); - r.cr1().write(|w| { - w.set_cen(true); - w.set_dir(Dir::UP) - }); - - this.set_ocm(Channel::Ch1, Ocm::PWMMODE1); - this.set_ocm(Channel::Ch2, Ocm::PWMMODE1); - this.set_ocm(Channel::Ch3, Ocm::PWMMODE1); - this.set_ocm(Channel::Ch4, Ocm::PWMMODE1); - } - this - } - - unsafe fn set_ocm(&mut self, channel: Channel, mode: Ocm) { - let r = T::regs(); - match channel { - Channel::Ch1 => r.ccmr_output(0).modify(|w| w.set_ocm(0, mode)), - Channel::Ch2 => r.ccmr_output(0).modify(|w| w.set_ocm(1, mode)), - Channel::Ch3 => r.ccmr_output(1).modify(|w| w.set_ocm(0, mode)), - Channel::Ch4 => r.ccmr_output(1).modify(|w| w.set_ocm(1, mode)), +impl Channel { + pub fn raw(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, + Channel::Ch3 => 2, + Channel::Ch4 => 3, } } +} - unsafe fn set_enable(&mut self, channel: Channel, enable: bool) { - let r = T::regs(); - match channel { - Channel::Ch1 => r.ccer().modify(|w| w.set_cce(0, enable)), - Channel::Ch2 => r.ccer().modify(|w| w.set_cce(1, enable)), - Channel::Ch3 => r.ccer().modify(|w| w.set_cce(2, enable)), - Channel::Ch4 => r.ccer().modify(|w| w.set_cce(3, enable)), - } - } +#[derive(Clone, Copy)] +pub enum OutputCompareMode { + Frozen, + ActiveOnMatch, + InactiveOnMatch, + Toggle, + ForceInactive, + ForceActive, + PwmMode1, + PwmMode2, +} - pub fn enable(&mut self, channel: Channel) { - unsafe { self.set_enable(channel, true) } - } - - pub fn disable(&mut self, channel: Channel) { - unsafe { self.set_enable(channel, false) } - } - - pub fn set_freq>(&mut self, freq: F) { - use core::convert::TryInto; - let clk = T::frequency(); - let r = T::regs(); - let freq: Hertz = freq.into(); - let ticks: u32 = clk.0 / freq.0; - let psc: u16 = (ticks / (1 << 16)).try_into().unwrap(); - let arr: u16 = (ticks / (u32::from(psc) + 1)).try_into().unwrap(); - unsafe { - r.psc().write(|w| w.set_psc(psc)); - r.arr().write(|w| w.set_arr(arr)); - } - } - - pub fn get_max_duty(&self) -> u32 { - let r = T::regs(); - unsafe { r.arr().read().arr() as u32 } - } - - pub fn set_duty(&mut self, channel: Channel, duty: u32) { - use core::convert::TryInto; - assert!(duty < self.get_max_duty()); - let duty: u16 = duty.try_into().unwrap(); - let r = T::regs(); - unsafe { - match channel { - Channel::Ch1 => r.ccr(0).modify(|w| w.set_ccr(duty)), - Channel::Ch2 => r.ccr(1).modify(|w| w.set_ccr(duty)), - Channel::Ch3 => r.ccr(2).modify(|w| w.set_ccr(duty)), - Channel::Ch4 => r.ccr(3).modify(|w| w.set_ccr(duty)), - } +impl From for stm32_metapac::timer::vals::Ocm { + fn from(mode: OutputCompareMode) -> Self { + match mode { + OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, + OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, + OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, + OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, + OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, + OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, + OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, + OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, } } } pub(crate) mod sealed { - pub trait Instance { - fn regs() -> crate::pac::timer::TimGp16; + use super::*; + + pub trait CaptureCompareCapable16bitInstance: + crate::timer::sealed::GeneralPurpose16bitInstance + { + unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + unsafe fn enable_channel(&mut self, channel: Channel, enable: bool); + + unsafe fn set_compare_value(&mut self, channel: Channel, value: u16); + + unsafe fn get_max_compare_value(&self) -> u16; + } + + pub trait CaptureCompareCapable32bitInstance: + crate::timer::sealed::GeneralPurpose32bitInstance + { + unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + unsafe fn enable_channel(&mut self, channel: Channel, enable: bool); + + unsafe fn set_compare_value(&mut self, channel: Channel, value: u32); + + unsafe fn get_max_compare_value(&self) -> u32; } } -pub trait Instance: sealed::Instance + Sized + RccPeripheral + 'static {} +pub trait CaptureCompareCapable16bitInstance: + sealed::CaptureCompareCapable16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static +{ +} +pub trait CaptureCompareCapable32bitInstance: + sealed::CaptureCompareCapable32bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static +{ +} #[allow(unused)] -macro_rules! impl_timer { +macro_rules! impl_compare_capable_16bit { ($inst:ident) => { - impl crate::pwm::sealed::Instance for crate::peripherals::$inst { - fn regs() -> crate::pac::timer::TimGp16 { - crate::pac::timer::TimGp16(crate::pac::$inst.0) + impl crate::pwm::sealed::CaptureCompareCapable16bitInstance for crate::peripherals::$inst { + unsafe fn set_output_compare_mode( + &mut self, + channel: crate::pwm::Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::GeneralPurpose16bitInstance; + let r = self.regs_gp16(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::GeneralPurpose16bitInstance; + self.regs_gp16() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) { + use crate::timer::sealed::GeneralPurpose16bitInstance; + self.regs_gp16() + .ccr(channel.raw()) + .modify(|w| w.set_ccr(value)); + } + + unsafe fn get_max_compare_value(&self) -> u16 { + use crate::timer::sealed::GeneralPurpose16bitInstance; + self.regs_gp16().arr().read().arr() } } - - impl crate::pwm::Instance for crate::peripherals::$inst {} }; } -pub trait PwmPin: gpio::OptionalPin { - unsafe fn configure(&mut self); -} +crate::pac::interrupts! { + ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { + impl_compare_capable_16bit!($inst); -impl PwmPin for gpio::NoPin { - unsafe fn configure(&mut self) {} + impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst { + + } + }; + + ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { + impl_compare_capable_16bit!($inst); + impl crate::pwm::sealed::CaptureCompareCapable32bitInstance for crate::peripherals::$inst { + unsafe fn set_output_compare_mode( + &mut self, + channel: crate::pwm::Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + self.regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + self.regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); + } + + unsafe fn set_compare_value(&mut self, channel: Channel, value: u32) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + self.regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + unsafe fn get_max_compare_value(&self) -> u32 { + use crate::timer::sealed::GeneralPurpose32bitInstance; + self.regs_gp32().arr().read().arr() as u32 + } + } + impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst { + + } + impl CaptureCompareCapable32bitInstance for crate::peripherals::$inst { + + } + }; + + ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { + impl_compare_capable_16bit!($inst); + impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst { + + } + }; } #[allow(unused)] -macro_rules! impl_pwm_pin { - ($timer:ident, $channel:ident, $pin:ident, $af:expr) => { - impl crate::pwm::PwmPin - for crate::peripherals::$pin - { - unsafe fn configure(&mut self) { - use crate::gpio::sealed::{AFType, Pin}; - use crate::gpio::Speed; - self.set_low(); - self.set_speed(Speed::VeryHigh); - self.set_as_af($af, AFType::OutputPushPull); - } - } +macro_rules! impl_pwm_nopin { + ($inst:ident) => { + impl_no_pin!($inst, Channel1Pin); + impl_no_pin!($inst, Channel1ComplementaryPin); + impl_no_pin!($inst, Channel2Pin); + impl_no_pin!($inst, Channel2ComplementaryPin); + impl_no_pin!($inst, Channel3Pin); + impl_no_pin!($inst, Channel3ComplementaryPin); + impl_no_pin!($inst, Channel4Pin); + impl_no_pin!($inst, Channel4ComplementaryPin); + impl_no_pin!($inst, ExternalTriggerPin); + impl_no_pin!($inst, BreakInputPin); + impl_no_pin!($inst, BreakInputComparator1Pin); + impl_no_pin!($inst, BreakInputComparator2Pin); + impl_no_pin!($inst, BreakInput2Pin); + impl_no_pin!($inst, BreakInput2Comparator1Pin); + impl_no_pin!($inst, BreakInput2Comparator2Pin); }; } crate::pac::peripherals!( - (timer, $inst:ident) => { impl_timer!($inst); }; + (timer, $inst:ident) => { impl_pwm_nopin!($inst); }; ); crate::pac::peripheral_pins!( - ($inst:ident, timer,TIM_GP16, $pin:ident, CH1, $af:expr) => { - impl_pwm_pin!($inst, Ch1, $pin, $af); + ($inst:ident, timer, $block:ident, $pin:ident, CH1, $af:expr) => { + impl_pin!($inst, Channel1Pin, $pin, $af); }; - ($inst:ident, timer,TIM_GP16, $pin:ident, CH2, $af:expr) => { - impl_pwm_pin!($inst, Ch2, $pin, $af); + ($inst:ident, timer, $block:ident, $pin:ident, CH1N, $af:expr) => { + impl_pin!($inst, Channel1ComplementaryPin, $pin, $af); }; - ($inst:ident, timer,TIM_GP16, $pin:ident, CH3, $af:expr) => { - impl_pwm_pin!($inst, Ch3, $pin, $af); + ($inst:ident, timer, $block:ident, $pin:ident, CH2, $af:expr) => { + impl_pin!($inst, Channel2Pin, $pin, $af); }; - ($inst:ident, timer,TIM_GP16, $pin:ident, CH4, $af:expr) => { - impl_pwm_pin!($inst, Ch4, $pin, $af); + ($inst:ident, timer, $block:ident, $pin:ident, CH2N, $af:expr) => { + impl_pin!($inst, Channel2ComplementaryPin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, CH3, $af:expr) => { + impl_pin!($inst, Channel3Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, CH3N, $af:expr) => { + impl_pin!($inst, Channel3ComplementaryPin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, CH4, $af:expr) => { + impl_pin!($inst, Channel4Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, CH4N, $af:expr) => { + impl_pin!($inst, Channel4ComplementaryPin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, ETR, $af:expr) => { + impl_pin!($inst, ExternalTriggerPin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN, $af:expr) => { + impl_pin!($inst, BreakInputPin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN_COMP1, $af:expr) => { + impl_pin!($inst, BreakInputComparator1Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN_COMP2, $af:expr) => { + impl_pin!($inst, BreakInputComparator2Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN2, $af:expr) => { + impl_pin!($inst, BreakInput2Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN2_COMP1, $af:expr) => { + impl_pin!($inst, BreakInput2Comparator1Pin, $pin, $af); + }; + ($inst:ident, timer, $block:ident, $pin:ident, BKIN2_COMP2, $af:expr) => { + impl_pin!($inst, BreakInput2Comparator2Pin, $pin, $af); }; ); diff --git a/embassy-stm32/src/pwm/pins.rs b/embassy-stm32/src/pwm/pins.rs new file mode 100644 index 00000000..77ba1f6d --- /dev/null +++ b/embassy-stm32/src/pwm/pins.rs @@ -0,0 +1,138 @@ +use crate::gpio::OptionalPin; + +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; +} + +pub(crate) mod sealed { + use crate::gpio::sealed::OptionalPin; + + pub trait Channel1Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait Channel1ComplementaryPin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait Channel2Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait Channel2ComplementaryPin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait Channel3Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait Channel3ComplementaryPin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait Channel4Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait Channel4ComplementaryPin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait ExternalTriggerPin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait BreakInputPin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait BreakInputComparator1Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait BreakInputComparator2Pin: OptionalPin { + unsafe fn configure(&mut self); + } + + pub trait BreakInput2Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait BreakInput2Comparator1Pin: OptionalPin { + unsafe fn configure(&mut self); + } + pub trait BreakInput2Comparator2Pin: OptionalPin { + unsafe fn configure(&mut self); + } +} +pub trait Channel1Pin: sealed::Channel1Pin + OptionalPin + 'static {} +pub trait Channel1ComplementaryPin: + sealed::Channel1ComplementaryPin + OptionalPin + 'static +{ +} + +pub trait Channel2Pin: sealed::Channel2Pin + 'static {} +pub trait Channel2ComplementaryPin: + sealed::Channel2ComplementaryPin + OptionalPin + 'static +{ +} + +pub trait Channel3Pin: sealed::Channel3Pin + 'static {} +pub trait Channel3ComplementaryPin: + sealed::Channel3ComplementaryPin + OptionalPin + 'static +{ +} + +pub trait Channel4Pin: sealed::Channel4Pin + 'static {} +pub trait Channel4ComplementaryPin: + sealed::Channel4ComplementaryPin + OptionalPin + 'static +{ +} + +pub trait ExternalTriggerPin: + sealed::ExternalTriggerPin + OptionalPin + 'static +{ +} + +pub trait BreakInputPin: sealed::BreakInputPin + OptionalPin + 'static {} +pub trait BreakInputComparator1Pin: + sealed::BreakInputComparator1Pin + OptionalPin + 'static +{ +} +pub trait BreakInputComparator2Pin: + sealed::BreakInputComparator2Pin + OptionalPin + 'static +{ +} + +pub trait BreakInput2Pin: sealed::BreakInput2Pin + OptionalPin + 'static {} +pub trait BreakInput2Comparator1Pin: + sealed::BreakInput2Comparator1Pin + OptionalPin + 'static +{ +} +pub trait BreakInput2Comparator2Pin: + sealed::BreakInput2Comparator2Pin + OptionalPin + 'static +{ +} + +macro_rules! impl_no_pin { + ($timer:ident, $signal:ident) => { + impl crate::pwm::pins::sealed::$signal for crate::gpio::NoPin { + unsafe fn configure(&mut self) {} + } + impl crate::pwm::pins::$signal for crate::gpio::NoPin {} + }; +} + +#[allow(unused)] +macro_rules! impl_pin { + ($timer:ident, $signal:ident, $pin:ident, $af:expr) => { + impl crate::pwm::pins::sealed::$signal + for crate::peripherals::$pin + { + unsafe fn configure(&mut self) { + use crate::gpio::sealed::{AFType, Pin}; + use crate::gpio::Speed; + self.set_low(); + self.set_speed(Speed::VeryHigh); + self.set_as_af($af, AFType::OutputPushPull); + } + } + + impl crate::pwm::pins::$signal for crate::peripherals::$pin {} + }; +} diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs new file mode 100644 index 00000000..3dc46e6e --- /dev/null +++ b/embassy-stm32/src/pwm/simple_pwm.rs @@ -0,0 +1,80 @@ +use crate::{ + pwm::{pins::*, CaptureCompareCapable16bitInstance, Channel, OutputCompareMode}, + time::Hertz, +}; +use core::marker::PhantomData; +use embassy::util::Unborrow; +use embassy_hal_common::unborrow; + +pub struct SimplePwm<'d, T> { + phantom: PhantomData<&'d mut T>, + inner: T, +} + +impl<'d, T: CaptureCompareCapable16bitInstance> SimplePwm<'d, T> { + pub fn new>( + tim: impl Unborrow + 'd, + ch1: impl Unborrow> + 'd, + ch2: impl Unborrow> + 'd, + ch3: impl Unborrow> + 'd, + ch4: impl Unborrow> + 'd, + freq: F, + ) -> Self { + unborrow!(tim, ch1, ch2, ch3, ch4); + + T::enable(); + ::reset(); + + unsafe { + ch1.configure(); + ch2.configure(); + ch3.configure(); + ch4.configure(); + } + + let mut this = Self { + inner: tim, + phantom: PhantomData, + }; + + this.inner.set_frequency(freq); + this.inner.start(); + + unsafe { + this.inner + .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + } + this + } + + pub fn enable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, true); + } + } + + pub fn disable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, false); + } + } + + pub fn set_freq>(&mut self, freq: F) { + self.inner.set_frequency(freq); + } + + pub fn get_max_duty(&self) -> u16 { + unsafe { self.inner.get_max_compare_value() } + } + + pub fn set_duty(&mut self, channel: Channel, duty: u16) { + assert!(duty < self.get_max_duty()); + unsafe { self.inner.set_compare_value(channel, duty) } + } +}