diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 424b1c99..a7b8b322 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -51,6 +51,8 @@ pub mod sdmmc; pub mod spi; #[cfg(usart)] pub mod usart; +//#[cfg(pwm)] +pub mod pwm; #[cfg(feature = "subghz")] pub mod subghz; diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs new file mode 100644 index 00000000..f1ef1f1d --- /dev/null +++ b/embassy-stm32/src/pwm/mod.rs @@ -0,0 +1,195 @@ +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; + +pub struct Pwm<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +// TIM2 + +pub struct Ch1 {} +pub struct Ch2 {} +pub struct Ch3 {} +pub struct Ch4 {} + +#[derive(Clone, Copy)] +pub enum Channel { + Ch1, + Ch2, + Ch3, + 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)), + } + } + + 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)), + } + } + + 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)), + } + } + } +} + +pub(crate) mod sealed { + pub trait Instance { + fn regs() -> crate::pac::timer::TimGp16; + } +} + +pub trait Instance: sealed::Instance + Sized + RccPeripheral + 'static {} + +#[allow(unused)] +macro_rules! impl_timer { + ($inst:ident) => { + impl crate::pwm::sealed::Instance for crate::peripherals::$inst { + fn regs() -> crate::pac::timer::TimGp16 { + crate::pac::$inst + } + } + + impl crate::pwm::Instance for crate::peripherals::$inst {} + }; +} + +pub trait PwmPin: gpio::OptionalPin { + unsafe fn configure(&mut self); +} + +impl PwmPin for gpio::NoPin { + unsafe fn configure(&mut self) {} +} + +#[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); + } + } + }; +} + +#[cfg(rcc_g0)] +mod impls { + crate::pac::peripherals!( + (timer, TIM2) => { impl_timer!(TIM2); }; + (timer, TIM3) => { impl_timer!(TIM3); }; + (timer, TIM4) => { impl_timer!(TIM4); }; + (timer, TIM5) => { impl_timer!(TIM5); }; + ); + + impl_pwm_pin!(TIM2, Ch1, PA0, 2); + impl_pwm_pin!(TIM2, Ch2, PA1, 2); + impl_pwm_pin!(TIM2, Ch3, PA2, 2); + impl_pwm_pin!(TIM2, Ch4, PA3, 2); +}