From 8211d58ee29362edc7f210e6b6d692881254f6da Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Wed, 29 Sep 2021 10:10:20 -0400 Subject: [PATCH 1/5] stm32/pwm: initial commit --- embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/pwm/mod.rs | 195 +++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 embassy-stm32/src/pwm/mod.rs 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); +} From 22fad1e7bc19ddcc5a8e09d4fbd9aa66c28f24bf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Nov 2021 03:04:50 +0100 Subject: [PATCH 2/5] stm32/pwm: impl instance/pin for all chips --- embassy-stm32/src/pwm/mod.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index f1ef1f1d..398c5ce7 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -179,17 +179,21 @@ macro_rules! impl_pwm_pin { }; } -#[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); }; - ); +crate::pac::peripherals!( + (timer, $inst:ident) => { impl_timer!($inst); }; +); - 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); -} +crate::pac::peripheral_pins!( + ($inst:ident, timer,TIM_GP16, $pin:ident, CH1, $af:expr) => { + impl_pwm_pin!($inst, Ch1, $pin, $af); + }; + ($inst:ident, timer,TIM_GP16, $pin:ident, CH2, $af:expr) => { + impl_pwm_pin!($inst, Ch2, $pin, $af); + }; + ($inst:ident, timer,TIM_GP16, $pin:ident, CH3, $af:expr) => { + impl_pwm_pin!($inst, Ch3, $pin, $af); + }; + ($inst:ident, timer,TIM_GP16, $pin:ident, CH4, $af:expr) => { + impl_pwm_pin!($inst, Ch4, $pin, $af); + }; +); From d7d12584118e7c460c441b8b12836680c8eecba3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Nov 2021 03:05:10 +0100 Subject: [PATCH 3/5] stm32/pwm: small cleanups --- embassy-stm32/src/lib.rs | 3 +-- embassy-stm32/src/pwm/mod.rs | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a7b8b322..649b25f1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -41,6 +41,7 @@ pub mod i2c; #[cfg(crc)] pub mod crc; +pub mod pwm; #[cfg(pwr)] pub mod pwr; #[cfg(rng)] @@ -51,8 +52,6 @@ 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 index 398c5ce7..7176603e 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -87,15 +87,11 @@ impl<'d, T: Instance> Pwm<'d, T> { } pub fn enable(&mut self, channel: Channel) { - unsafe { - self.set_enable(channel, true); - } + unsafe { self.set_enable(channel, true) } } pub fn disable(&mut self, channel: Channel) { - unsafe { - self.set_enable(channel, false); - } + unsafe { self.set_enable(channel, false) } } pub fn set_freq>(&mut self, freq: F) { From e40555e245b9a0c351f2cf250231453a0ec78a3d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Nov 2021 03:05:26 +0100 Subject: [PATCH 4/5] examples/stm32g4: add pwm example --- examples/stm32g4/Cargo.toml | 2 +- examples/stm32g4/src/bin/pwm.rs | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 examples/stm32g4/src/bin/pwm.rs diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 0f9d77f5..f4378309 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ 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", "time-driver-tim2", "stm32g491re", "memory-x", "unstable-pac"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "time-driver-tim3", "stm32g491re", "memory-x", "unstable-pac"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } defmt = "0.3" diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs new file mode 100644 index 00000000..1aa7b85f --- /dev/null +++ b/examples/stm32g4/src/bin/pwm.rs @@ -0,0 +1,36 @@ +#![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::{Channel, Pwm}; +use embassy_stm32::time::U32Ext; +use embassy_stm32::Peripherals; +use example_common::*; + +#[embassy::main] +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 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; + } +} From 006e567716362acfde90afcfd8a25be1fe0d6897 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Nov 2021 03:06:53 +0100 Subject: [PATCH 5/5] stm32/pwm: allow using the advanced timer instances too. --- .vscode/settings.json | 1 + embassy-stm32/src/pwm/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0e67ab82..87dd158e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "rust-analyzer.checkOnSave.allTargets": false, "rust-analyzer.checkOnSave.command": "clippy", "rust-analyzer.cargo.noDefaultFeatures": true, + "rust-analyzer.experimental.procAttrMacros": false, "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.target": "thumbv7em-none-eabi", "rust-analyzer.cargo.features": [ diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 7176603e..8357b6cd 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -142,7 +142,7 @@ macro_rules! impl_timer { ($inst:ident) => { impl crate::pwm::sealed::Instance for crate::peripherals::$inst { fn regs() -> crate::pac::timer::TimGp16 { - crate::pac::$inst + crate::pac::timer::TimGp16(crate::pac::$inst.0) } }