From a1f7a94c6924a5344ed29b60e11be673ca04bfcb Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 8 Dec 2021 17:36:40 +0100 Subject: [PATCH 1/4] Add low level timer API. --- embassy-stm32/src/lib.rs | 1 + embassy-stm32/src/timer/mod.rs | 224 +++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 embassy-stm32/src/timer/mod.rs diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1ed48df3..dfc02773 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -20,6 +20,7 @@ pub mod gpio; pub mod rcc; #[cfg(feature = "_time-driver")] mod time_driver; +pub mod timer; // Sometimes-present hardware diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs new file mode 100644 index 00000000..ed2cb2d9 --- /dev/null +++ b/embassy-stm32/src/timer/mod.rs @@ -0,0 +1,224 @@ +use embassy::interrupt::Interrupt; + +use crate::rcc::{sealed::RccPeripheral as __RccPeri, RccPeripheral}; +use crate::time::Hertz; +use stm32_metapac::timer::vals; + +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; +} + +pub(crate) mod sealed { + use super::*; + pub trait Basic16bitInstance: RccPeripheral { + type Interrupt: Interrupt; + + fn regs(&self) -> crate::pac::timer::TimBasic; + + fn start(&mut self); + + fn stop(&mut self); + + fn reset(&mut self); + + fn set_frequency>(&mut self, frequency: F); + + fn clear_update_interrupt(&mut self) -> bool; + + fn enable_update_interrupt(&mut self, enable: bool); + } + + pub trait GeneralPurpose16bitInstance: Basic16bitInstance { + fn regs_gp16(&self) -> crate::pac::timer::TimGp16; + } + + pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { + fn regs_gp32(&self) -> crate::pac::timer::TimGp32; + + fn set_frequency>(&mut self, frequency: F); + } + + pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { + fn regs_advanced(&self) -> crate::pac::timer::TimAdv; + } +} + +pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} + +pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} + +pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} + +pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} + +#[allow(unused)] +macro_rules! impl_basic_16bit_timer { + ($inst:ident, $irq:ident) => { + impl sealed::Basic16bitInstance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::$irq; + + fn regs(&self) -> crate::pac::timer::TimBasic { + crate::pac::timer::TimBasic(crate::pac::$inst.0) + } + + fn start(&mut self) { + unsafe { + self.regs().cr1().modify(|r| r.set_cen(true)); + } + } + + fn stop(&mut self) { + let regs = self.regs(); + unsafe { + regs.cr1().modify(|r| r.set_cen(false)); + } + } + + fn reset(&mut self) { + unsafe { + self.regs().cnt().write(|r| r.set_cnt(0)); + } + } + + fn set_frequency>(&mut self, frequency: F) { + use core::convert::TryInto; + let f = frequency.into().0; + let timer_f = Self::frequency().0; + let pclk_ticks_per_timer_period = timer_f / f; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); + let arr: u16 = + unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into()); + + let regs = self.regs(); + unsafe { + regs.psc().write(|r| r.set_psc(psc)); + regs.arr().write(|r| r.set_arr(arr)); + + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + } + + fn clear_update_interrupt(&mut self) -> bool { + unsafe { + let sr = self.regs().sr().read(); + if sr.uif() { + self.regs().sr().modify(|r| { + r.set_uif(false); + }); + true + } else { + false + } + } + } + + fn enable_update_interrupt(&mut self, enable: bool) { + unsafe { + self.regs().dier().write(|r| r.set_uie(enable)); + } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_32bit_timer { + ($inst:ident) => { + impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst { + fn regs_gp32(&self) -> crate::pac::timer::TimGp32 { + crate::pac::$inst + } + + fn set_frequency>(&mut self, frequency: F) { + use core::convert::TryInto; + let f = frequency.into().0; + let timer_f = Self::frequency().0; + let pclk_ticks_per_timer_period = (timer_f / f) as u64; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); + let arr: u32 = + unwrap!(((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into())); + + let regs = self.regs_gp32(); + unsafe { + regs.psc().write(|r| r.set_psc(psc)); + regs.arr().write(|r| r.set_arr(arr)); + + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + } + } + }; +} + +crate::pac::interrupts! { + ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { + impl_basic_16bit_timer!($inst, $irq); + + impl Basic16bitInstance for crate::peripherals::$inst { + } + }; + ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { + impl_basic_16bit_timer!($inst, $irq); + + impl Basic16bitInstance for crate::peripherals::$inst { + } + + impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { + fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { + crate::pac::$inst + } + } + + impl GeneralPurpose16bitInstance for crate::peripherals::$inst { + } + }; + + ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { + impl_basic_16bit_timer!($inst, $irq); + + impl Basic16bitInstance for crate::peripherals::$inst { + } + + impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { + fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { + crate::pac::timer::TimGp16(crate::pac::$inst.0) + } + } + + impl GeneralPurpose16bitInstance for crate::peripherals::$inst { + } + + impl_32bit_timer!($inst); + + impl GeneralPurpose32bitInstance for crate::peripherals::$inst { + } + }; + + ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { + impl_basic_16bit_timer!($inst, $irq); + + impl Basic16bitInstance for crate::peripherals::$inst { + } + + impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { + fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { + crate::pac::timer::TimGp16(crate::pac::$inst.0) + } + } + + impl GeneralPurpose16bitInstance for crate::peripherals::$inst { + } + impl sealed::AdvancedControlInstance for crate::peripherals::$inst { + fn regs_advanced(&self) -> crate::pac::timer::TimAdv { + crate::pac::$inst + } + } + impl AdvancedControlInstance for crate::peripherals::$inst { + } + }; +} From 16d09f074a845abc4e03010de9a3e4804807a57e Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 8 Dec 2021 17:37:46 +0100 Subject: [PATCH 2/4] Add simple PWM, add PWM pin definitions also accessible from low-level API. --- embassy-stm32/src/pwm/mod.rs | 362 ++++++++++++++++------------ embassy-stm32/src/pwm/pins.rs | 138 +++++++++++ embassy-stm32/src/pwm/simple_pwm.rs | 80 ++++++ 3 files changed, 432 insertions(+), 148 deletions(-) create mode 100644 embassy-stm32/src/pwm/pins.rs create mode 100644 embassy-stm32/src/pwm/simple_pwm.rs 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) } + } +} From e07df92651f58eb001ea8c22cae0130435877b17 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 8 Dec 2021 17:38:12 +0100 Subject: [PATCH 3/4] Make RCC accessible using low-level API. --- embassy-stm32/src/rcc/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d0b6e5a1..e5bf15c8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -85,6 +85,11 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { &*CLOCK_FREQS.as_ptr() } +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; +} + pub(crate) mod sealed { pub trait RccPeripheral { fn frequency() -> crate::time::Hertz; From e056bedd553e7fbc7d28f8f516c87fcd12859aef Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 8 Dec 2021 17:39:59 +0100 Subject: [PATCH 4/4] Port the PWM example to H7, add low-level API example implementing 32-bit PWM. --- examples/stm32g4/src/bin/pwm.rs | 4 +- examples/stm32h7/Cargo.toml | 3 +- .../stm32h7/src/bin/low_level_timer_api.rs | 147 ++++++++++++++++++ examples/stm32h7/src/bin/pwm.rs | 48 ++++++ 4 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 examples/stm32h7/src/bin/low_level_timer_api.rs create mode 100644 examples/stm32h7/src/bin/pwm.rs diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 1aa7b85f..3dd45318 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -7,7 +7,7 @@ mod example_common; use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy_stm32::gpio::NoPin; -use embassy_stm32::pwm::{Channel, Pwm}; +use embassy_stm32::pwm::{simple_pwm::SimplePwm, Channel}; use embassy_stm32::time::U32Ext; use embassy_stm32::Peripherals; use example_common::*; @@ -16,7 +16,7 @@ use example_common::*; async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); - let mut pwm = Pwm::new(p.TIM2, p.PA5, NoPin, NoPin, NoPin, 10000.hz()); + let mut pwm = SimplePwm::new(p.TIM2, p.PA5, NoPin, NoPin, NoPin, 10000.hz()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index a34ac6ad..de294318 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,8 +10,9 @@ resolver = "2" [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743zi", "net", "time-driver-tim2", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743zi", "net", "time-driver-tim2", "exti", "unstable-pac"] } embassy-net = { path = "../../embassy-net", default-features = false, features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } +embassy-hal-common = { path = "../../embassy-hal-common", default-features = false, features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs new file mode 100644 index 00000000..2640f249 --- /dev/null +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -0,0 +1,147 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use core::marker::PhantomData; + +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy::util::Unborrow; +use embassy_hal_common::unborrow; +use embassy_stm32::gpio::NoPin; +use embassy_stm32::pwm::{pins::*, Channel, OutputCompareMode}; +use embassy_stm32::time::{Hertz, U32Ext}; +use embassy_stm32::timer::GeneralPurpose32bitInstance; +use embassy_stm32::{Config, Peripherals}; +use example_common::*; + +pub fn config() -> Config { + let mut config = Config::default(); + config.rcc.sys_ck = Some(400.mhz().into()); + config.rcc.hclk = Some(400.mhz().into()); + config.rcc.pll1.q_ck = Some(100.mhz().into()); + config.rcc.pclk1 = Some(100.mhz().into()); + config.rcc.pclk2 = Some(100.mhz().into()); + config.rcc.pclk3 = Some(100.mhz().into()); + config.rcc.pclk4 = Some(100.mhz().into()); + config +} + +#[embassy::main(config = "config()")] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let mut pwm = SimplePwm32::new(p.TIM5, p.PA0, NoPin, NoPin, NoPin, 10000.hz()); + let max = pwm.get_max_duty(); + pwm.enable(Channel::Ch1); + + info!("PWM initialized"); + info!("PWM max duty {}", max); + + loop { + pwm.set_duty(Channel::Ch1, 0); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 4); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 2); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max - 1); + Timer::after(Duration::from_millis(300)).await; + } +} +pub struct SimplePwm32<'d, T: GeneralPurpose32bitInstance> { + phantom: PhantomData<&'d mut T>, + inner: T, +} + +impl<'d, T: GeneralPurpose32bitInstance> SimplePwm32<'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.set_freq(freq); + this.inner.start(); + + unsafe { + this.inner + .regs_gp32() + .ccmr_output(0) + .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); + this.inner + .regs_gp32() + .ccmr_output(0) + .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); + this.inner + .regs_gp32() + .ccmr_output(1) + .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); + this.inner + .regs_gp32() + .ccmr_output(1) + .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); + } + this + } + + pub fn enable(&mut self, channel: Channel) { + unsafe { + self.inner + .regs_gp32() + .ccer() + .modify(|w| w.set_cce(channel.raw(), true)); + } + } + + pub fn disable(&mut self, channel: Channel) { + unsafe { + self.inner + .regs_gp32() + .ccer() + .modify(|w| w.set_cce(channel.raw(), false)); + } + } + + pub fn set_freq>(&mut self, freq: F) { + ::set_frequency( + &mut self.inner, + freq, + ); + } + + pub fn get_max_duty(&self) -> u32 { + unsafe { self.inner.regs_gp32().arr().read().arr() } + } + + pub fn set_duty(&mut self, channel: Channel, duty: u32) { + defmt::assert!(duty < self.get_max_duty()); + unsafe { + self.inner + .regs_gp32() + .ccr(channel.raw()) + .modify(|w| w.set_ccr(duty)) + } + } +} diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs new file mode 100644 index 00000000..020150a3 --- /dev/null +++ b/examples/stm32h7/src/bin/pwm.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_stm32::gpio::NoPin; +use embassy_stm32::pwm::{simple_pwm::SimplePwm, Channel}; +use embassy_stm32::time::U32Ext; +use embassy_stm32::{Config, Peripherals}; +use example_common::*; + +pub fn config() -> Config { + let mut config = Config::default(); + config.rcc.sys_ck = Some(400.mhz().into()); + config.rcc.hclk = Some(400.mhz().into()); + config.rcc.pll1.q_ck = Some(100.mhz().into()); + config.rcc.pclk1 = Some(100.mhz().into()); + config.rcc.pclk2 = Some(100.mhz().into()); + config.rcc.pclk3 = Some(100.mhz().into()); + config.rcc.pclk4 = Some(100.mhz().into()); + config +} + +#[embassy::main(config = "config()")] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let mut pwm = SimplePwm::new(p.TIM3, p.PA6, NoPin, NoPin, NoPin, 10000.hz()); + let max = pwm.get_max_duty(); + pwm.enable(Channel::Ch1); + + info!("PWM initialized"); + info!("PWM max duty {}", max); + + loop { + pwm.set_duty(Channel::Ch1, 0); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 4); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 2); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max - 1); + Timer::after(Duration::from_millis(300)).await; + } +}