Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions embassy-stm32/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1273,18 +1273,18 @@ fn main() {
(("timer", "BKIN2"), quote!(crate::timer::BreakInputPin<BkIn2>)),
(("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn2>)),
(("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn2>)),
(("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)),
(("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)),
(("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)),
(("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)),
(("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)),
(("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)),
(("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)),
(("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)),
(("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)),
(("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
(("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)),
(("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)),
(("hrtim", "CHA1"), quote!(crate::hrtim::HRTimerPin<ChA>)),
(("hrtim", "CHA2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChA>)),
(("hrtim", "CHB1"), quote!(crate::hrtim::HRTimerPin<ChB>)),
(("hrtim", "CHB2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChB>)),
(("hrtim", "CHC1"), quote!(crate::hrtim::HRTimerPin<ChC>)),
(("hrtim", "CHC2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChC>)),
(("hrtim", "CHD1"), quote!(crate::hrtim::HRTimerPin<ChD>)),
(("hrtim", "CHD2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChD>)),
(("hrtim", "CHE1"), quote!(crate::hrtim::HRTimerPin<ChE>)),
(("hrtim", "CHE2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChE>)),
(("hrtim", "CHF1"), quote!(crate::hrtim::HRTimerPin<ChF>)),
(("hrtim", "CHF2"), quote!(crate::hrtim::HRTimerComplementaryPin<ChF>)),
(("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)),
(("lptim", "CH2"), quote!(crate::lptim::Channel2Pin)),
(("lptim", "OUT"), quote!(crate::lptim::OutputPin)),
Expand Down
109 changes: 109 additions & 0 deletions embassy-stm32/src/hrtim/advanced_channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//! AdvancedChannel

use super::{Instance, Prescaler};
use crate::time::Hertz;

trait SealedAdvancedChannel<T: Instance> {
fn raw() -> usize;
}

/// Advanced channel instance trait.
#[allow(private_bounds)]
pub trait AdvancedChannel<T: Instance>: SealedAdvancedChannel<T> {
/// Channel index
fn index() -> usize {
Self::raw()
}

/// Set channel prescaler
fn set_channel_prescaler(channel: usize, ckpsc: Prescaler) {
T::regs().tim(channel).cr().modify(|w| w.set_ckpsc(ckpsc.into()))
}

/// Set channel period
fn set_channel_period(channel: usize, per: u16) {
T::regs().tim(channel).per().modify(|w| w.set_per(per));
}

/// Set channel frequency
fn set_channel_frequency(channel: usize, frequency: Hertz) {
let f = frequency.0;

// TODO: wire up HRTIM to the RCC mux infra.
//#[cfg(stm32f334)]
//let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(T::frequency()).0;
//#[cfg(not(stm32f334))]
let timer_f = T::frequency().0;

let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
let psc = if T::regs().isr().read().dllrdy() {
Prescaler::compute_min_high_res(psc_min)
} else {
Prescaler::compute_min_low_res(psc_min)
};

let timer_f = 32 * (timer_f as u64 / psc as u64);
let per: u16 = (timer_f / f as u64) as u16;

Self::set_channel_prescaler(channel, psc);
Self::set_channel_period(channel, per);
}

/// Set the dead time as a proportion of max_duty
fn set_channel_dead_time(channel: usize, dead_time: u16) {
let regs = T::regs();

let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into();

// The dead-time base clock runs 4 times slower than the hrtim base clock
// u9::MAX = 511
let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511);
let psc = if T::regs().isr().read().dllrdy() {
Prescaler::compute_min_high_res(psc_min)
} else {
Prescaler::compute_min_low_res(psc_min)
};

let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32);

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

#[allow(unused)]
trait SealedAdvancedChannelMaster<T: Instance> {}

/// Advanced channel instance trait.
#[allow(private_bounds)]
#[allow(unused)]
pub trait AdvancedChannelMaster<T: Instance>: SealedAdvancedChannelMaster<T> {
/// Set master frequency
fn set_master_frequency(&mut self, frequency: Hertz) {
let f = frequency.0;

// TODO: wire up HRTIM to the RCC mux infra.
//#[cfg(stm32f334)]
//let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(T::frequency()).0;
//#[cfg(not(stm32f334))]
let timer_f = T::frequency().0;

let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
let psc = if T::regs().isr().read().dllrdy() {
Prescaler::compute_min_high_res(psc_min)
} else {
Prescaler::compute_min_low_res(psc_min)
};

let timer_f = 32 * (timer_f as u64 / psc as u64);
let per: u16 = (timer_f / f as u64) as u16;

let regs = T::regs();

regs.mcr().modify(|w| w.set_ckpsc(psc.into()));
regs.mper().modify(|w| w.set_mper(per));
}
}
45 changes: 45 additions & 0 deletions embassy-stm32/src/hrtim/advanced_pwm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! AdvancedPwm

use embassy_hal_internal::Peri;

#[cfg(hrtim_v2)]
use super::ChF;
use super::low_level::HrTimer;
use super::{ChA, ChB, ChC, ChD, ChE, Instance};
use crate::hrtim::PwmPin;

/// Struct used to divide a high resolution timer into multiple channels
pub struct AdvancedPwm<'d, T: Instance> {
_inner: HrTimer<'d, T>,
}

impl<'d, T: Instance> AdvancedPwm<'d, T> {
/// Create a new HRTIM driver.
///
/// This splits the HRTIM into its constituent parts, which you can then use individually.
pub fn new(
tim: Peri<'d, T>,
_cha: Option<if_afio!(PwmPin<'d, T, ChA, A>)>,
_chb: Option<if_afio!(PwmPin<'d, T, ChB, A>)>,
_chc: Option<if_afio!(PwmPin<'d, T, ChC, A>)>,
_chd: Option<if_afio!(PwmPin<'d, T, ChD, A>)>,
_che: Option<if_afio!(PwmPin<'d, T, ChE, A>)>,
#[cfg(hrtim_v2)] _chf: Option<if_afio!(PwmPin<'d, T, ChF, A>)>,
) -> Self {
Self::new_inner(tim)
}

fn new_inner(tim: Peri<'d, T>) -> Self {
let mut tim = HrTimer::new(tim);

#[cfg(stm32f334)]
if crate::pac::RCC.cfgr3().read().hrtim1sw() == crate::pac::rcc::vals::Timsw::PLL1_P {
tim.calibrate();
}

#[cfg(not(stm32f334))]
tim.calibrate();

Self { _inner: tim }
}
}
129 changes: 55 additions & 74 deletions embassy-stm32/src/hrtim/bridge_converter.rs
Original file line number Diff line number Diff line change
@@ -1,90 +1,65 @@
//! Fixed-frequency bridge converter driver.
//!
//! Our implementation of the bridge converter uses a single channel and three 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.
use stm32_hrtim::compare_register::HrCompareRegister;
use stm32_hrtim::control::{HrPwmControl, HrPwmCtrl};
use stm32_hrtim::output::{HrOutput, Output1Pin, Output2Pin};
use stm32_hrtim::timer::{HrTim, HrTimer};
use stm32_hrtim::{HrParts, HrPwmAdvExt, HrPwmBuilder, HrtimPrescaler, PreloadSource, capture};

use crate::hrtim::HrPwmBuilderExt;
use crate::peripherals::HRTIM1;
use crate::rcc::SealedRccPeripheral;

use core::marker::PhantomData;

use super::{AdvancedChannel, Instance};
use crate::time::Hertz;

/// Fixed-frequency bridge converter driver.
pub struct BridgeConverter<TIM, PSCL> {
timer: HrParts<TIM, PSCL>,
///
/// Our implementation of the bridge converter uses a single channel and three 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: Instance, C: AdvancedChannel<T>> {
timer: PhantomData<T>,
channel: PhantomData<C>,
dead_time: u16,
primary_duty: u16,
min_secondary_duty: u16,
max_secondary_duty: u16,
}

impl<TIM: HrPwmAdvExt, PSCL: HrtimPrescaler> BridgeConverter<TIM, PSCL>
where
TIM: stm32_hrtim::timer::InstanceX + HrPwmAdvExt<PreloadSource = PreloadSource>,
HrTim<TIM, PSCL, capture::NoDma, capture::NoDma>: HrTimer,
{
impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
/// Create a new HRTIM bridge converter driver.
pub fn new<P1, P2>(
timer: TIM,
pin1: P1,
pin2: P2,
frequency: Hertz,
prescaler: PSCL,
hr_control: &mut HrPwmControl,
) -> Self
where
P1: Output1Pin<TIM>,
P2: Output2Pin<TIM>,
HrPwmBuilder<TIM, PSCL, PreloadSource, P1, P2>: HrPwmBuilderExt<TIM, PSCL, P1, P2>,
{
let f = frequency.0;

let timer_f = HRTIM1::frequency().0;

let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
let psc = PSCL::VALUE as u32;
assert!(
psc >= psc_min,
"Prescaler set too low to be able to reach target frequency"
);

let timer_f = 32 * (timer_f / psc);
let per: u16 = (timer_f / f) as u16;

let mut timer = timer
.pwm_advanced(pin1, pin2)
.prescaler(prescaler)
.period(per)
.preload(PreloadSource::OnRepetitionUpdate)
.finalize(hr_control);
pub fn new(_channel: C, frequency: Hertz) -> Self {
C::set_channel_frequency(C::index(), frequency);

// Always enable preload
T::regs().tim(C::index()).cr().modify(|w| {
w.set_preen(true);
w.set_repu(true);
w.set_cont(true);
});

// Enable timer outputs
T::regs().oenr().modify(|w| {
w.set_t1oen(C::index(), true);
w.set_t2oen(C::index(), 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
timer.out1.enable_set_event(&timer.timer);
T::regs().tim(C::index()).setr(0).modify(|w| w.set_per(true));

// Set output 1 to inactive on a compare 1 event
timer.out1.enable_rst_event(&timer.cr1);
T::regs().tim(C::index()).rstr(0).modify(|w| w.set_cmp(0, true));

// Set output 2 to active on a compare 2 event
timer.out2.enable_set_event(&timer.cr2);
T::regs().tim(C::index()).setr(1).modify(|w| w.set_cmp(1, true));

// Set output 2 to inactive on a compare 3 event
timer.out2.enable_rst_event(&timer.cr3);
T::regs().tim(C::index()).rstr(1).modify(|w| w.set_cmp(2, true));

Self {
timer,
timer: PhantomData,
channel: PhantomData,
dead_time: 0,
primary_duty: 0,
min_secondary_duty: 0,
Expand All @@ -93,19 +68,18 @@ where
}

/// Start HRTIM.
pub fn start(&mut self, hr_control: &mut HrPwmCtrl) {
self.timer.timer.start(hr_control);
pub fn start(&mut self) {
T::regs().mcr().modify(|w| w.set_tcen(C::index(), true));
}

/// Stop HRTIM.
pub fn stop(&mut self, hr_control: &mut HrPwmCtrl) {
self.timer.timer.stop(hr_control);
pub fn stop(&mut self) {
T::regs().mcr().modify(|w| w.set_tcen(C::index(), false));
}

/*
/// Enable burst mode.
pub fn enable_burst_mode(&mut self) {
T::regs().tim(C::raw()).outr().modify(|w| {
T::regs().tim(C::index()).outr().modify(|w| {
// Enable Burst Mode
w.set_idlem(0, true);
w.set_idlem(1, true);
Expand All @@ -118,17 +92,24 @@ where

/// Disable burst mode.
pub fn disable_burst_mode(&mut self) {
T::regs().tim(C::raw()).outr().modify(|w| {
T::regs().tim(C::index()).outr().modify(|w| {
// Disable Burst Mode
w.set_idlem(0, false);
w.set_idlem(1, false);
})
}*/
}

fn update_primary_duty_or_dead_time(&mut self) {
self.min_secondary_duty = self.primary_duty + self.dead_time;
self.timer.cr1.set_duty(self.primary_duty);
self.timer.cr2.set_duty(self.min_secondary_duty);

T::regs()
.tim(C::index())
.cmp(0)
.modify(|w| w.set_cmp(self.primary_duty));
T::regs()
.tim(C::index())
.cmp(1)
.modify(|w| w.set_cmp(self.min_secondary_duty));
}

/// Set the dead time as a proportion of the maximum compare value
Expand All @@ -140,7 +121,7 @@ where

/// Get the maximum compare value of a duty cycle
pub fn get_max_compare_value(&mut self) -> u16 {
self.timer.timer.get_period()
T::regs().tim(C::index()).per().read().per()
}

/// The primary duty is the period in which the primary switch is active
Expand All @@ -165,6 +146,6 @@ where
secondary_duty
};

self.timer.cr3.set_duty(secondary_duty);
T::regs().tim(C::index()).cmp(2).modify(|w| w.set_cmp(secondary_duty));
}
}
Loading