use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; pub struct ComplementaryPwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, phantom: PhantomData<(Perip, Channel)>, } macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| unsafe { pin.set_low(); pin.set_as_af(pin.af_num(), AFType::OutputPushPull); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); ComplementaryPwmPin { _pin: pin.map_into(), phantom: PhantomData, } } } }; } complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, } impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _ch1: Option>, _ch1n: Option>, _ch2: Option>, _ch2n: Option>, _ch3: Option>, _ch3n: Option>, _ch4: Option>, _ch4n: Option>, freq: Hertz, ) -> Self { Self::new_inner(tim, freq) } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { into_ref!(tim); T::enable(); ::reset(); let mut this = Self { inner: tim }; this.inner.set_frequency(freq); this.inner.start(); unsafe { this.inner.enable_outputs(true); this.inner .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); this.inner .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); this.inner .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); this.inner .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); } this } pub fn enable(&mut self, channel: Channel) { unsafe { self.inner.enable_channel(channel, true); self.inner.enable_complementary_channel(channel, true); } } pub fn disable(&mut self, channel: Channel) { unsafe { self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } } pub fn set_freq(&mut self, freq: Hertz) { self.inner.set_frequency(freq); } pub fn get_max_duty(&self) -> u16 { unsafe { self.inner.get_max_compare_value() } } pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty < self.get_max_duty()); unsafe { self.inner.set_compare_value(channel, duty) } } /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { let (ckd, value) = compute_dead_time_value(value); unsafe { self.inner.set_dead_time_clock_division(ckd); self.inner.set_dead_time_value(value); } } } fn compute_dead_time_value(value: u16) -> (Ckd, u8) { /* Dead-time = T_clk * T_dts * T_dtg T_dts: This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters (ETR, TIx), 00: tDTS=tCK_INT 01: tDTS=2*tCK_INT 10: tDTS=4*tCK_INT T_dtg: This bit-field defines the duration of the dead-time inserted between the complementary outputs. DT correspond to this duration. DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS. DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS. DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS. DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS. Example if TDTS=125ns (8MHz), dead-time possible values are: 0 to 15875 ns by 125 ns steps, 16 us to 31750 ns by 250 ns steps, 32 us to 63us by 1 us steps, 64 us to 126 us by 2 us steps */ let mut error = u16::MAX; let mut ckd = Ckd::DIV1; let mut bits = 0u8; for this_ckd in [Ckd::DIV1, Ckd::DIV2, Ckd::DIV4] { let outdiv = match this_ckd { Ckd::DIV1 => 1, Ckd::DIV2 => 2, Ckd::DIV4 => 4, _ => unreachable!(), }; // 127 // 128 // .. // 254 // 256 // .. // 504 // 512 // .. // 1008 let target = value / outdiv; let (these_bits, result) = if target < 128 { (target as u8, target) } else if target < 255 { (64 + (target / 2) as u8, (target - target % 2)) } else if target < 508 { (32 + (target / 8) as u8, (target - target % 8)) } else if target < 1008 { (32 + (target / 16) as u8, (target - target % 16)) } else { (u8::MAX, 1008) }; let this_error = value.abs_diff(result * outdiv); if error > this_error { ckd = this_ckd; bits = these_bits; error = this_error; } match error { 0 => break, _ => {} } } (ckd, bits) } #[cfg(test)] mod tests { use super::{compute_dead_time_value, Ckd}; #[test] fn test_compute_dead_time_value() { struct test_run { value: u16, ckd: Ckd, bits: u8, } let fn_results = [ test_run { value: 1, ckd: Ckd::DIV1, bits: 1, }, test_run { value: 125, ckd: Ckd::DIV1, bits: 125, }, test_run { value: 245, ckd: Ckd::DIV1, bits: 64 + 245 / 2, }, test_run { value: 255, ckd: Ckd::DIV2, bits: 127, }, test_run { value: 400, ckd: Ckd::DIV1, bits: 32 + (400u16 / 8) as u8, }, test_run { value: 600, ckd: Ckd::DIV4, bits: 64 + (600u16 / 8) as u8, }, ]; for test_run in fn_results { let (ckd, bits) = compute_dead_time_value(test_run.value); assert_eq!(ckd.0, test_run.ckd.0); assert_eq!(bits, test_run.bits); } } }