hrtim: fix example and auto adjust psc.

This commit is contained in:
xoviat 2023-07-01 21:47:44 -05:00
parent 8141d53d94
commit aceba1c03f
4 changed files with 160 additions and 56 deletions

View File

@ -144,6 +144,18 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> {
T::enable(); T::enable();
<T as crate::rcc::sealed::RccPeripheral>::reset(); <T as crate::rcc::sealed::RccPeripheral>::reset();
// // Enable and and stabilize the DLL
// T::regs().dllcr().modify(|w| {
// // w.set_calen(true);
// // w.set_calrte(11);
// w.set_cal(true);
// });
//
// debug!("wait for dll calibration");
// while !T::regs().isr().read().dllrdy() {}
//
// debug!("dll calibration complete");
Self { Self {
_inner: tim, _inner: tim,
master: Master { phantom: PhantomData }, master: Master { phantom: PhantomData },
@ -173,12 +185,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance> BurstController<T> {
/// light loading conditions, and that the low-side switch must be active for a short time to drive /// light loading conditions, and that the low-side switch must be active for a short time to drive
/// a bootstrapped high-side switch. /// a bootstrapped high-side switch.
pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> { pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
phantom: PhantomData<T>, timer: PhantomData<T>,
pub ch: C, channel: PhantomData<C>,
dead_time: u16,
primary_duty: u16,
} }
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> { impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
pub fn new(channel: C, frequency: Hertz) -> Self { pub fn new(_channel: C, frequency: Hertz) -> Self {
use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect};
T::set_channel_frequency(C::raw(), frequency); T::set_channel_frequency(C::raw(), frequency);
@ -186,15 +200,21 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
// Always enable preload // Always enable preload
T::regs().tim(C::raw()).cr().modify(|w| { T::regs().tim(C::raw()).cr().modify(|w| {
w.set_preen(true); w.set_preen(true);
w.set_repu(true);
w.set_cont(Cont::CONTINUOUS); w.set_cont(Cont::CONTINUOUS);
}); });
// Enable timer outputs
T::regs().oenr().modify(|w| { T::regs().oenr().modify(|w| {
w.set_t1oen(C::raw(), true); w.set_t1oen(C::raw(), true);
w.set_t2oen(C::raw(), true); w.set_t2oen(C::raw(), true);
}); });
// The dead-time generation unit cannot be used because it forces the other output
// to be completely complementary to the first output, which restricts certain waveforms
// Therefore, software-implemented dead time must be used when setting the duty cycles
// Set output 1 to active on a period event // Set output 1 to active on a period event
T::regs() T::regs()
.tim(C::raw()) .tim(C::raw())
@ -207,21 +227,23 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
.rstr(0) .rstr(0)
.modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE));
// Set output 2 to active on a compare 1 event // Set output 2 to active on a compare 2 event
T::regs() T::regs()
.tim(C::raw()) .tim(C::raw())
.setr(1) .setr(1)
.modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE));
// Set output 2 to inactive on a compare 2 event // Set output 2 to inactive on a compare 3 event
T::regs() T::regs()
.tim(C::raw()) .tim(C::raw())
.rstr(1) .rstr(1)
.modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE));
Self { Self {
phantom: PhantomData, timer: PhantomData,
ch: channel, channel: PhantomData,
dead_time: 0,
primary_duty: 0,
} }
} }
@ -236,7 +258,6 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
pub fn enable_burst_mode(&mut self) { pub fn enable_burst_mode(&mut self) {
use crate::pac::hrtim::vals::{Idlem, Idles}; use crate::pac::hrtim::vals::{Idlem, Idles};
// TODO: fix metapac
T::regs().tim(C::raw()).outr().modify(|w| { T::regs().tim(C::raw()).outr().modify(|w| {
w.set_idlem(0, Idlem::SETIDLE); w.set_idlem(0, Idlem::SETIDLE);
w.set_idlem(1, Idlem::SETIDLE); w.set_idlem(1, Idlem::SETIDLE);
@ -258,9 +279,18 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
}) })
} }
fn update_primary_duty_or_dead_time(&mut self) {
T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty));
T::regs()
.tim(C::raw())
.cmp(1)
.modify(|w| w.set_cmp(self.primary_duty + self.dead_time));
}
/// Set the dead time as a proportion of the maximum compare value /// Set the dead time as a proportion of the maximum compare value
pub fn set_dead_time(&mut self, value: u16) { pub fn set_dead_time(&mut self, dead_time: u16) {
T::set_channel_dead_time(C::raw(), value); self.dead_time = dead_time;
self.update_primary_duty_or_dead_time();
} }
/// Get the maximum compare value of a duty cycle /// Get the maximum compare value of a duty cycle
@ -272,15 +302,17 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
/// ///
/// In the case of a buck converter, this is the high-side switch /// 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 /// In the case of a boost converter, this is the low-side switch
pub fn set_primary_duty(&mut self, primary: u16) { pub fn set_primary_duty(&mut self, primary_duty: u16) {
T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); self.primary_duty = primary_duty;
self.update_primary_duty_or_dead_time();
} }
/// The primary duty is the period in any switch is active /// The secondary 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 /// 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) { /// If a fully complementary output is desired, the secondary duty can be set to the max compare
T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); pub fn set_secondary_duty(&mut self, secondary_duty: u16) {
T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty));
} }
} }
@ -290,14 +322,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
/// but does not include secondary rectification, which is appropriate for applications /// but does not include secondary rectification, which is appropriate for applications
/// with a low-voltage on the secondary side. /// with a low-voltage on the secondary side.
pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> { pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
phantom: PhantomData<T>, timer: PhantomData<T>,
channel: PhantomData<C>,
min_period: u16, min_period: u16,
max_period: u16, max_period: u16,
pub ch: C,
} }
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> { impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
use crate::pac::hrtim::vals::Cont; use crate::pac::hrtim::vals::Cont;
T::set_channel_frequency(C::raw(), min_frequency); T::set_channel_frequency(C::raw(), min_frequency);
@ -305,19 +337,30 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Resona
// Always enable preload // Always enable preload
T::regs().tim(C::raw()).cr().modify(|w| { T::regs().tim(C::raw()).cr().modify(|w| {
w.set_preen(true); w.set_preen(true);
w.set_repu(true);
w.set_cont(Cont::CONTINUOUS); w.set_cont(Cont::CONTINUOUS);
w.set_half(true); w.set_half(true);
}); });
// Enable timer outputs
T::regs().oenr().modify(|w| {
w.set_t1oen(C::raw(), true);
w.set_t2oen(C::raw(), true);
});
// Dead-time generator can be used in this case because the primary fets
// of a resonant converter are always complementary
T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true));
let max_period = T::regs().tim(C::raw()).per().read().per(); let max_period = T::regs().tim(C::raw()).per().read().per();
let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;
Self { Self {
timer: PhantomData,
channel: PhantomData,
min_period: min_period, min_period: min_period,
max_period: max_period, max_period: max_period,
phantom: PhantomData,
ch: channel,
} }
} }

View File

@ -123,7 +123,7 @@ impl From<u8> for HighResolutionControlPrescaler {
#[cfg(hrtim_v1)] #[cfg(hrtim_v1)]
impl HighResolutionControlPrescaler { impl HighResolutionControlPrescaler {
pub fn compute_min(val: u32) -> Self { pub fn compute_min_high_res(val: u32) -> Self {
*[ *[
HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div1,
HighResolutionControlPrescaler::Div2, HighResolutionControlPrescaler::Div2,
@ -139,6 +139,18 @@ impl HighResolutionControlPrescaler {
.next() .next()
.unwrap() .unwrap()
} }
pub fn compute_min_low_res(val: u32) -> Self {
*[
HighResolutionControlPrescaler::Div32,
HighResolutionControlPrescaler::Div64,
HighResolutionControlPrescaler::Div128,
]
.iter()
.skip_while(|psc| <HighResolutionControlPrescaler as Into<u32>>::into(**psc) <= val)
.next()
.unwrap()
}
} }
pub(crate) mod sealed { pub(crate) mod sealed {
@ -367,10 +379,14 @@ foreach_interrupt! {
let f = frequency.0; let f = frequency.0;
let timer_f = Self::frequency().0; let timer_f = Self::frequency().0;
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
let psc = HighResolutionControlPrescaler::compute_min(psc_min); let psc = if Self::regs().isr().read().dllrdy() {
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
} else {
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
};
let psc_val: u32 = psc.into(); let psc_val: u32 = psc.into();
let timer_f = timer_f / psc_val; let timer_f = 32 * (timer_f / psc_val);
let per: u16 = (timer_f / f) as u16; let per: u16 = (timer_f / f) as u16;
let regs = Self::regs(); let regs = Self::regs();
@ -386,10 +402,14 @@ foreach_interrupt! {
let f = frequency.0; let f = frequency.0;
let timer_f = Self::frequency().0; let timer_f = Self::frequency().0;
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
let psc = HighResolutionControlPrescaler::compute_min(psc_min); let psc = if Self::regs().isr().read().dllrdy() {
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
} else {
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
};
let psc_val: u32 = psc.into(); let psc_val: u32 = psc.into();
let timer_f = timer_f / psc_val; let timer_f = 32 * (timer_f / psc_val);
let per: u16 = (timer_f / f) as u16; let per: u16 = (timer_f / f) as u16;
let regs = Self::regs(); let regs = Self::regs();
@ -410,7 +430,12 @@ foreach_interrupt! {
// The dead-time base clock runs 4 times slower than the hrtim base clock // The dead-time base clock runs 4 times slower than the hrtim base clock
// u9::MAX = 511 // u9::MAX = 511
let psc_min = (psc_val * dead_time as u32) / (4 * 511); let psc_min = (psc_val * dead_time as u32) / (4 * 511);
let psc = HighResolutionControlPrescaler::compute_min(psc_min); let psc = if Self::regs().isr().read().dllrdy() {
HighResolutionControlPrescaler::compute_min_high_res(psc_min)
} else {
HighResolutionControlPrescaler::compute_min_low_res(psc_min)
};
let dt_psc_val: u32 = psc.into(); let dt_psc_val: u32 = psc.into();
let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);

View File

@ -0,0 +1,27 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");
let p = embassy_stm32::init(Default::default());
let mut out1 = Output::new(p.PA8, Level::Low, Speed::High);
out1.set_high();
Timer::after(Duration::from_millis(500)).await;
out1.set_low();
Timer::after(Duration::from_millis(500)).await;
info!("end program");
cortex_m::asm::bkpt();
}

View File

@ -5,12 +5,20 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::pwm::advanced_pwm::*; use embassy_stm32::pwm::advanced_pwm::*;
use embassy_stm32::time::khz; use embassy_stm32::time::{khz, mhz};
use embassy_stm32::Config;
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default()); let mut config: Config = Default::default();
config.rcc.sysclk = Some(mhz(64));
config.rcc.hclk = Some(mhz(64));
config.rcc.pclk1 = Some(mhz(32));
config.rcc.pclk2 = Some(mhz(64));
let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
let ch1 = PwmPin::new_cha(p.PA8); let ch1 = PwmPin::new_cha(p.PA8);
@ -29,34 +37,35 @@ async fn main(_spawner: Spawner) {
None, None,
); );
let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); info!("pwm constructed");
buck_converter.set_primary_duty(0); let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5));
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 // embassy_stm32::pac::HRTIM1
let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); // .tim(0)
// .setr(0)
boost_converter.set_primary_duty(0); // .modify(|w| w.set_sst(Activeeffect::SETACTIVE));
boost_converter.set_secondary_duty(0);
// let max = pwm.get_max_duty();
// pwm.set_dead_time(max / 1024);
// //
// pwm.enable(Channel::Ch1); // Timer::after(Duration::from_millis(500)).await;
// //
// info!("PWM initialized"); // embassy_stm32::pac::HRTIM1
// info!("PWM max duty {}", max); // .tim(0)
// // .rstr(0)
// loop { // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE));
// pwm.set_duty(Channel::Ch1, 0);
// Timer::after(Duration::from_millis(300)).await; let max_duty = buck_converter.get_max_compare_value();
// pwm.set_duty(Channel::Ch1, max / 4);
// Timer::after(Duration::from_millis(300)).await; info!("max compare value: {}", max_duty);
// pwm.set_duty(Channel::Ch1, max / 2);
// Timer::after(Duration::from_millis(300)).await; buck_converter.set_dead_time(max_duty / 20);
// pwm.set_duty(Channel::Ch1, max - 1); buck_converter.set_primary_duty(max_duty / 2);
// Timer::after(Duration::from_millis(300)).await; buck_converter.set_secondary_duty(3 * max_duty / 4);
// }
buck_converter.start();
Timer::after(Duration::from_millis(500)).await;
info!("end program");
cortex_m::asm::bkpt();
} }