stm32/hrtim: impl. bridge, dead-time part. res.

This commit is contained in:
xoviat 2023-06-29 21:05:41 -05:00
parent 3252eaa060
commit 8c4997c5fc
3 changed files with 241 additions and 78 deletions

View file

@ -9,33 +9,44 @@ use crate::gpio::AnyPin;
use crate::time::Hertz;
use crate::Peripheral;
// Re-implement the channels for hrtim
pub struct Master<T: AdvancedCaptureCompare16bitInstance> {
pub enum Source {
Master,
ChA,
ChB,
ChC,
ChD,
ChE,
}
pub struct BurstController<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChA<T: AdvancedCaptureCompare16bitInstance> {
pub struct Master<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChB<T: AdvancedCaptureCompare16bitInstance> {
pub struct ChA<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChC<T: AdvancedCaptureCompare16bitInstance> {
pub struct ChB<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChD<T: AdvancedCaptureCompare16bitInstance> {
pub struct ChC<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChE<T: AdvancedCaptureCompare16bitInstance> {
pub struct ChD<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
pub struct ChE<T: HighResolutionCaptureCompare16bitInstance> {
phantom: PhantomData<T>,
}
mod sealed {
use crate::pwm::AdvancedCaptureCompare16bitInstance;
use crate::pwm::HighResolutionCaptureCompare16bitInstance;
pub trait AdvancedChannel<T: AdvancedCaptureCompare16bitInstance> {}
pub trait AdvancedChannel<T: HighResolutionCaptureCompare16bitInstance> {}
}
pub trait AdvancedChannel<T: AdvancedCaptureCompare16bitInstance>: sealed::AdvancedChannel<T> {
pub trait AdvancedChannel<T: HighResolutionCaptureCompare16bitInstance>: sealed::AdvancedChannel<T> {
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<Perip>> {
impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel<Perip>> {
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + '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<Perip>> {
impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel<Perip>> {
pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
into_ref!(pin);
critical_section::with(|_| {
@ -83,8 +94,8 @@ macro_rules! advanced_channel_impl {
}
}
impl<T: AdvancedCaptureCompare16bitInstance> sealed::AdvancedChannel<T> for $channel<T> {}
impl<T: AdvancedCaptureCompare16bitInstance> AdvancedChannel<T> for $channel<T> {
impl<T: HighResolutionCaptureCompare16bitInstance> sealed::AdvancedChannel<T> for $channel<T> {}
impl<T: HighResolutionCaptureCompare16bitInstance> AdvancedChannel<T> for $channel<T> {
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<T>,
pub burst_controller: BurstController<T>,
pub ch_a: ChA<T>,
pub ch_b: ChB<T>,
pub ch_c: ChC<T>,
@ -109,7 +121,7 @@ pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> {
pub ch_e: ChE<T>,
}
impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> {
impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> {
pub fn new(
tim: impl Peripheral<P = T> + 'd,
_cha: Option<PwmPin<'d, T, ChA<T>>>,
@ -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<T: HighResolutionCaptureCompare16bitInstance> BurstController<T> {
pub fn set_source(&mut self, source: Source) {
let regs = T::regs();
}
}
// Represents a fixed-frequency bridge converter
pub struct BridgeConverter<T: AdvancedCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
/// 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<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
phantom: PhantomData<T>,
pub ch: C,
}
impl<T: AdvancedCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
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<T: AdvancedCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
/// 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<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
phantom: PhantomData<T>,
min_period: u16,
max_period: u16,
pub ch: C,
}
impl<T: AdvancedCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
pub fn new(channel: C, min_frequency: Hertz) -> Self {
impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
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
}
}

View file

@ -76,11 +76,9 @@ pub(crate) enum HighResolutionControlPrescaler {
}
#[cfg(hrtim_v1)]
impl ops::Div<HighResolutionControlPrescaler> for Hertz {
type Output = Hertz;
fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output {
let divisor = match rhs {
impl From<HighResolutionControlPrescaler> 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<HighResolutionControlPrescaler> for Hertz {
HighResolutionControlPrescaler::Div32 => 32,
HighResolutionControlPrescaler::Div64 => 64,
HighResolutionControlPrescaler::Div128 => 128,
};
Hertz(self.0 / divisor)
}
}
}
@ -111,9 +107,26 @@ impl From<HighResolutionControlPrescaler> for u8 {
}
}
#[cfg(hrtim_v1)]
impl From<u8> 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| <HighResolutionControlPrescaler as Into<u32>>::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)]

View file

@ -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);