From 8c4997c5fcd8c25d68865b9e7537e0add50eac24 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 29 Jun 2023 21:05:41 -0500 Subject: [PATCH] stm32/hrtim: impl. bridge, dead-time part. res. --- embassy-stm32/src/pwm/advanced_pwm.rs | 193 +++++++++++++++++++++----- embassy-stm32/src/pwm/mod.rs | 117 +++++++++++----- examples/stm32f334/src/bin/pwm.rs | 9 +- 3 files changed, 241 insertions(+), 78 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index bb0e06ad..228899c0 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -9,33 +9,44 @@ use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; -// Re-implement the channels for hrtim -pub struct Master { +pub enum Source { + Master, + ChA, + ChB, + ChC, + ChD, + ChE, +} + +pub struct BurstController { phantom: PhantomData, } -pub struct ChA { +pub struct Master { phantom: PhantomData, } -pub struct ChB { +pub struct ChA { phantom: PhantomData, } -pub struct ChC { +pub struct ChB { phantom: PhantomData, } -pub struct ChD { +pub struct ChC { phantom: PhantomData, } -pub struct ChE { +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { phantom: PhantomData, } mod sealed { - use crate::pwm::AdvancedCaptureCompare16bitInstance; + use crate::pwm::HighResolutionCaptureCompare16bitInstance; - pub trait AdvancedChannel {} + pub trait AdvancedChannel {} } -pub trait AdvancedChannel: sealed::AdvancedChannel { +pub trait AdvancedChannel: sealed::AdvancedChannel { fn raw() -> usize; } @@ -51,7 +62,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -67,7 +78,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -83,8 +94,8 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel { fn raw() -> usize { $ch_num } @@ -99,9 +110,10 @@ advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { +pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { _inner: PeripheralRef<'d, T>, pub master: Master, + pub burst_controller: BurstController, pub ch_a: ChA, pub ch_b: ChB, pub ch_c: ChC, @@ -109,7 +121,7 @@ pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { pub ch_e: ChE, } -impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _cha: Option>>, @@ -135,6 +147,7 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { Self { _inner: tim, master: Master { phantom: PhantomData }, + burst_controller: BurstController { phantom: PhantomData }, ch_a: ChA { phantom: PhantomData }, ch_b: ChB { phantom: PhantomData }, ch_c: ChC { phantom: PhantomData }, @@ -142,54 +155,162 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ch_e: ChE { phantom: PhantomData }, } } +} - /// Set the dead time as a proportion of max_duty - pub fn set_dead_time(&mut self, _value: u16) { - todo!() - // let (ckd, value) = compute_dead_time_value(value); - // - // self.inner.set_dead_time_clock_division(ckd); - // self.inner.set_dead_time_value(value); +impl BurstController { + pub fn set_source(&mut self, source: Source) { + let regs = T::regs(); } } -// Represents a fixed-frequency bridge converter -pub struct BridgeConverter> { +/// Represents a fixed-frequency bridge converter +/// +/// Our implementation of the bridge converter uses a single channel and two compare registers, +/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous +/// conduction mode. +/// +/// It is important to remember that in synchronous topologies, energy can flow in reverse during +/// light loading conditions, and that the low-side switch must be active for a short time to drive +/// a bootstrapped high-side switch. +pub struct BridgeConverter> { phantom: PhantomData, pub ch: C, } -impl> BridgeConverter { +impl> BridgeConverter { pub fn new(channel: C, frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; + + T::set_channel_frequency(C::raw(), frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + }); + + // Set output 1 to active on a period event + T::regs() + .tim(C::raw()) + .setr(0) + .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + + // Set output 1 to inactive on a compare 1 event + T::regs() + .tim(C::raw()) + .rstr(0) + .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + + // Set output 2 to active on a compare 1 event + T::regs() + .tim(C::raw()) + .setr(1) + .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); + + // Set output 2 to inactive on a compare 2 event + T::regs() + .tim(C::raw()) + .rstr(1) + .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); + Self { phantom: PhantomData, ch: channel, } } - pub fn set_duty(&mut self, primary: u16, secondary: u16) { - let _ = T::regs(); - let _ = C::raw(); + pub fn start(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true)); + } - todo!() + pub fn stop(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_compare_value(&mut self) -> u16 { + T::regs().tim(C::raw()).per().read().per() + } + + /// The primary duty is the period in which the primary switch is active + /// + /// In the case of a buck converter, this is the high-side switch + /// In the case of a boost converter, this is the low-side switch + pub fn set_primary_duty(&mut self, primary: u16) { + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); + } + + /// The primary duty is the period in any switch is active + /// + /// If less than or equal to the primary duty, the secondary switch will never be active + pub fn set_secondary_duty(&mut self, secondary: u16) { + T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); } } -// Represents a variable-frequency resonant converter -pub struct ResonantConverter> { +/// Represents a variable-frequency resonant converter +/// +/// This implementation of a resonsant converter is appropriate for a half or full bridge, +/// but does not include secondary rectification, which is appropriate for applications +/// with a low-voltage on the secondary side. +pub struct ResonantConverter> { phantom: PhantomData, + min_period: u16, + max_period: u16, pub ch: C, } -impl> ResonantConverter { - pub fn new(channel: C, min_frequency: Hertz) -> Self { +impl> ResonantConverter { + pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::Cont; + + T::set_channel_frequency(C::raw(), min_frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + w.set_half(true); + }); + + // TODO: compute min period value + Self { + min_period: 0, + max_period: T::regs().tim(C::raw()).per().read().per(), phantom: PhantomData, ch: channel, } } - pub fn set_frequency(&mut self, frequency: Hertz) { - todo!() + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + pub fn set_period(&mut self, period: u16) { + assert!(period < self.max_period); + assert!(period > self.min_period); + + T::regs().tim(C::raw()).per().modify(|w| w.set_per(period)); + } + + /// Get the minimum compare value of a duty cycle + pub fn get_min_period(&mut self) -> u16 { + self.min_period + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_period(&mut self) -> u16 { + self.max_period } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 67009e78..2586dc15 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -76,11 +76,9 @@ pub(crate) enum HighResolutionControlPrescaler { } #[cfg(hrtim_v1)] -impl ops::Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { - let divisor = match rhs { +impl From for u32 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { HighResolutionControlPrescaler::Div1 => 1, HighResolutionControlPrescaler::Div2 => 2, HighResolutionControlPrescaler::Div4 => 4, @@ -89,9 +87,7 @@ impl ops::Div for Hertz { HighResolutionControlPrescaler::Div32 => 32, HighResolutionControlPrescaler::Div64 => 64, HighResolutionControlPrescaler::Div128 => 128, - }; - - Hertz(self.0 / divisor) + } } } @@ -111,9 +107,26 @@ impl From for u8 { } } +#[cfg(hrtim_v1)] +impl From for HighResolutionControlPrescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => HighResolutionControlPrescaler::Div1, + 0b001 => HighResolutionControlPrescaler::Div2, + 0b010 => HighResolutionControlPrescaler::Div4, + 0b011 => HighResolutionControlPrescaler::Div8, + 0b100 => HighResolutionControlPrescaler::Div16, + 0b101 => HighResolutionControlPrescaler::Div32, + 0b110 => HighResolutionControlPrescaler::Div64, + 0b111 => HighResolutionControlPrescaler::Div128, + _ => unreachable!(), + } + } +} + #[cfg(hrtim_v1)] impl HighResolutionControlPrescaler { - pub fn compute_min(base_f: Hertz, frequency: Hertz) -> Self { + pub fn compute_min(val: u32) -> Self { *[ HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div2, @@ -125,7 +138,7 @@ impl HighResolutionControlPrescaler { HighResolutionControlPrescaler::Div128, ] .iter() - .skip_while(|psc| frequency <= base_f / **psc) + .skip_while(|psc| >::into(**psc) <= val) .next() .unwrap() } @@ -135,11 +148,14 @@ pub(crate) mod sealed { use super::*; #[cfg(hrtim_v1)] - pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { fn set_master_frequency(frequency: Hertz); fn set_channel_frequency(channnel: usize, frequency: Hertz); + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + // fn enable_outputs(enable: bool); // // fn enable_channel(&mut self, channel: usize, enable: bool); @@ -178,7 +194,10 @@ pub(crate) mod sealed { } #[cfg(hrtim_v1)] -pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} +pub trait HighResolutionCaptureCompare16bitInstance: + sealed::HighResolutionCaptureCompare16bitInstance + 'static +{ +} pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static @@ -343,20 +362,19 @@ foreach_interrupt! { }; ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl crate::pwm::sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { fn set_master_frequency(frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -369,23 +387,46 @@ foreach_interrupt! { use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); regs.tim(channel).per().modify(|w| w.set_per(per)); } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let regs = Self::regs(); + + let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } } - impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { } }; @@ -411,16 +452,16 @@ pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); mod hrtim_pins { use super::*; - pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); } #[cfg(hrtim_v1)] diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index cc2ea861..20a62137 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -31,16 +31,17 @@ async fn main(_spawner: Spawner) { None, ); - pwm.set_dead_time(0); - let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); - buck_converter.set_duty(0, u16::MAX); + buck_converter.set_primary_duty(0); + buck_converter.set_secondary_duty(0); + buck_converter.set_dead_time(0); // note: if the pins are not passed into the advanced pwm struct, they will not be output let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); - boost_converter.set_duty(0, 0); + boost_converter.set_primary_duty(0); + boost_converter.set_secondary_duty(0); // let max = pwm.get_max_duty(); // pwm.set_dead_time(max / 1024);