|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | + |
| 4 | +// Requires a jumper from A1<->A2 (arduino naming) aka PA1<->PA4 |
| 5 | + |
| 6 | +#[path = "../examples/utils/mod.rs"] |
| 7 | +mod utils; |
| 8 | + |
| 9 | +mod common; |
| 10 | + |
| 11 | +use common::test_pwm; |
| 12 | +use fugit::{ExtU32, HertzU32, MicrosDurationU32}; |
| 13 | +use stm32_hrtim::compare_register::HrCompareRegister; |
| 14 | +use stm32_hrtim::control::HrPwmControl; |
| 15 | +use stm32_hrtim::deadtime::DeadtimeConfig; |
| 16 | +use stm32_hrtim::output::HrOutput; |
| 17 | +use stm32_hrtim::timer::HrTimer; |
| 18 | +use stm32_hrtim::{HrParts, HrPwmAdvExt as _, Pscl64}; |
| 19 | +use stm32g4xx_hal::delay::SYSTDelayExt; |
| 20 | +use stm32g4xx_hal::gpio::{GpioExt, PinExt}; |
| 21 | +use stm32g4xx_hal::hrtim::HrControltExt; |
| 22 | +use stm32g4xx_hal::pwr::PwrExt; |
| 23 | +use stm32g4xx_hal::rcc::{self, Rcc, RccExt}; |
| 24 | + |
| 25 | +use hal::stm32; |
| 26 | +use stm32g4xx_hal as hal; |
| 27 | + |
| 28 | +const PERIOD: u16 = 60_000; |
| 29 | +const F_SYS: HertzU32 = HertzU32::MHz(120); |
| 30 | +const CYCLES_PER_US: u32 = F_SYS.raw() / 1_000_000; |
| 31 | +type Timer = crate::common::Timer<CYCLES_PER_US>; |
| 32 | + |
| 33 | +#[embedded_test::tests] |
| 34 | +mod tests { |
| 35 | + use embedded_hal::delay::DelayNs; |
| 36 | + |
| 37 | + #[test] |
| 38 | + fn simple() { |
| 39 | + use super::*; |
| 40 | + |
| 41 | + let mut cp = stm32::CorePeripherals::take().unwrap(); |
| 42 | + let dp = stm32::Peripherals::take().unwrap(); |
| 43 | + let timer = Timer::enable_timer(&mut cp); |
| 44 | + |
| 45 | + // Set system frequency to 16MHz * 15/1/2 = 120MHz |
| 46 | + // This would lead to HrTim running at 120MHz * 32 = 3.84... |
| 47 | + let mut rcc = setup_rcc_120MHz(dp.PWR, dp.RCC); |
| 48 | + assert_eq!(rcc.clocks.sys_clk, F_SYS); |
| 49 | + let mut delay = cp.SYST.delay(&rcc.clocks); |
| 50 | + |
| 51 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 52 | + let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input(); |
| 53 | + let pin = gpioa.pa8; |
| 54 | + let pin_num = pin.pin_id(); |
| 55 | + |
| 56 | + let ( |
| 57 | + HrParts { |
| 58 | + timer: mut hrtimer, |
| 59 | + mut cr1, |
| 60 | + mut out, |
| 61 | + .. |
| 62 | + }, |
| 63 | + mut hr_control, |
| 64 | + ) = setup(pin, None, dp.HRTIM_TIMA, dp.HRTIM_COMMON, &mut rcc); |
| 65 | + |
| 66 | + cr1.set_duty(PERIOD / 2); |
| 67 | + out.enable_rst_event(&cr1); // Set low on compare match with cr1 |
| 68 | + out.enable_set_event(&hrtimer); // Set high at new period |
| 69 | + out.enable(); |
| 70 | + hrtimer.start(&mut hr_control.control); |
| 71 | + |
| 72 | + let t_hi = 500.micros(); |
| 73 | + let t_lo = 500.micros(); |
| 74 | + let t_max_deviation = 2.micros(); |
| 75 | + test_pwm(&timer, pin_num, t_lo, t_hi, t_max_deviation, 10); |
| 76 | + |
| 77 | + delay.delay_ms(20); // Give the host some time to read defmt messages |
| 78 | + } |
| 79 | + |
| 80 | + #[test] |
| 81 | + fn deadtime_68us_68us() { |
| 82 | + use super::*; |
| 83 | + |
| 84 | + deadtime_test(68, 68); |
| 85 | + } |
| 86 | + |
| 87 | + #[test] |
| 88 | + fn deadtime_68us_1us() { |
| 89 | + use super::*; |
| 90 | + |
| 91 | + deadtime_test(68, 1); |
| 92 | + } |
| 93 | + |
| 94 | + #[test] |
| 95 | + fn deadtime_1us_68us() { |
| 96 | + use super::*; |
| 97 | + |
| 98 | + deadtime_test(1, 68); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +#[allow(non_snake_case)] |
| 103 | +fn setup_rcc_120MHz(pwr: stm32::PWR, rcc: stm32::RCC) -> Rcc { |
| 104 | + defmt::info!("rcc"); |
| 105 | + // Set system frequency to 16MHz * 15/1/2 = 120MHz |
| 106 | + // This would lead to HrTim running at 120MHz * 32 = 3.84... |
| 107 | + let pwr = pwr.constrain().freeze(); |
| 108 | + rcc.freeze( |
| 109 | + rcc::Config::pll().pll_cfg(rcc::PllConfig { |
| 110 | + mux: rcc::PllSrc::HSI, |
| 111 | + n: rcc::PllNMul::MUL_15, |
| 112 | + m: rcc::PllMDiv::DIV_1, |
| 113 | + r: Some(rcc::PllRDiv::DIV_2), |
| 114 | + |
| 115 | + ..Default::default() |
| 116 | + }), |
| 117 | + pwr, |
| 118 | + ) |
| 119 | +} |
| 120 | + |
| 121 | +fn setup<P: stm32g4xx_hal::hrtim::HrtimPin<stm32::HRTIM_TIMA>>( |
| 122 | + pins: P, |
| 123 | + deadtime_cfg: Option<DeadtimeConfig>, |
| 124 | + hrtim_tima: stm32::HRTIM_TIMA, |
| 125 | + hrtim_common: stm32::HRTIM_COMMON, |
| 126 | + rcc: &mut Rcc, |
| 127 | +) -> (HrParts<stm32::HRTIM_TIMA, Pscl64, P::Out<Pscl64>>, HrPwmControl) { |
| 128 | + use stm32g4xx_hal::hrtim::HrPwmBuilderExt; |
| 129 | + let (hr_control, ..) = hrtim_common.hr_control(rcc).wait_for_calibration(); |
| 130 | + let mut hr_control = hr_control.constrain(); |
| 131 | + |
| 132 | + // ...with a prescaler of 64 this gives us a HrTimer with a tick rate of 60MHz |
| 133 | + // With a period of 60_000 set, this would be 60MHz/60_000 = 1kHz |
| 134 | + let prescaler = Pscl64; |
| 135 | + |
| 136 | + let mut hr_tim_builder: stm32_hrtim::HrPwmBuilder< |
| 137 | + stm32::HRTIM_TIMA, |
| 138 | + Pscl64, |
| 139 | + stm32_hrtim::PreloadSource, |
| 140 | + P, |
| 141 | + > = hrtim_tima |
| 142 | + .pwm_advanced(pins) |
| 143 | + .prescaler(prescaler) |
| 144 | + .period(PERIOD); |
| 145 | + if let Some(dt) = deadtime_cfg { |
| 146 | + hr_tim_builder = hr_tim_builder.deadtime(dt); |
| 147 | + } |
| 148 | + (hr_tim_builder.finalize(&mut hr_control), hr_control) |
| 149 | +} |
| 150 | + |
| 151 | +fn deadtime_test(deadtime_rising_us: u32, deadtime_falling_us: u32) { |
| 152 | + let mut cp = stm32::CorePeripherals::take().unwrap(); |
| 153 | + let dp = stm32::Peripherals::take().unwrap(); |
| 154 | + let timer = Timer::enable_timer(&mut cp); |
| 155 | + |
| 156 | + let deadtime_cfg = DeadtimeConfig::default() |
| 157 | + .prescaler(stm32_hrtim::deadtime::DeadtimePrescaler::ThrtimMul16) |
| 158 | + .deadtime_rising_value( |
| 159 | + (deadtime_rising_us * F_SYS.to_MHz() / 16) |
| 160 | + .try_into() |
| 161 | + .unwrap(), |
| 162 | + ) // ~(1/120MHz)*16*500 ~= 67us |
| 163 | + .deadtime_falling_value( |
| 164 | + (deadtime_falling_us * F_SYS.to_MHz() / 16) |
| 165 | + .try_into() |
| 166 | + .unwrap(), |
| 167 | + ); |
| 168 | + |
| 169 | + let mut rcc = setup_rcc_120MHz(dp.PWR, dp.RCC); |
| 170 | + assert_eq!(rcc.clocks.sys_clk, F_SYS); |
| 171 | + |
| 172 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 173 | + let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input(); |
| 174 | + let pin = gpioa.pa8; |
| 175 | + let pin_num = pin.pin_id(); |
| 176 | + let complementary_pin = gpioa.pa9; |
| 177 | + let complementary_pin_num = complementary_pin.pin_id(); |
| 178 | + |
| 179 | + let ( |
| 180 | + HrParts { |
| 181 | + timer: mut hrtimer, |
| 182 | + mut cr1, |
| 183 | + out: (mut out1, mut out2), |
| 184 | + .. |
| 185 | + }, |
| 186 | + mut hr_control, |
| 187 | + ) = setup( |
| 188 | + (pin, complementary_pin), |
| 189 | + Some(deadtime_cfg), |
| 190 | + dp.HRTIM_TIMA, |
| 191 | + dp.HRTIM_COMMON, |
| 192 | + &mut rcc, |
| 193 | + ); |
| 194 | + |
| 195 | + cr1.set_duty(PERIOD / 2); |
| 196 | + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 |
| 197 | + out1.enable_set_event(&hrtimer); // Set high at new period |
| 198 | + out1.enable(); |
| 199 | + out2.enable(); |
| 200 | + hrtimer.start(&mut hr_control.control); |
| 201 | + |
| 202 | + let t_max_deviation = 2.micros(); |
| 203 | + { |
| 204 | + let t_hi = (500 - deadtime_rising_us).micros(); |
| 205 | + let t_lo = (500 + deadtime_rising_us).micros(); |
| 206 | + test_pwm(&timer, pin_num, t_lo, t_hi, t_max_deviation, 10); |
| 207 | + } |
| 208 | + |
| 209 | + { |
| 210 | + let t_hi = (500 - deadtime_falling_us).micros(); |
| 211 | + let t_lo = (500 + deadtime_falling_us).micros(); |
| 212 | + test_pwm( |
| 213 | + &timer, |
| 214 | + complementary_pin_num, |
| 215 | + t_lo, |
| 216 | + t_hi, |
| 217 | + t_max_deviation, |
| 218 | + 10, |
| 219 | + ); |
| 220 | + } |
| 221 | + |
| 222 | + let gpioa = unsafe { &*stm32::GPIOA::PTR }; |
| 223 | + |
| 224 | + // Check that both output are not active at the same time |
| 225 | + let before = timer.now(); |
| 226 | + while (timer.now() - before) < MicrosDurationU32::millis(1000) { |
| 227 | + let idr = gpioa.idr().read(); |
| 228 | + |
| 229 | + let p = idr.idr(pin_num).is_high(); |
| 230 | + let compl_p = idr.idr(complementary_pin_num).is_high(); |
| 231 | + defmt::assert!(!(p && compl_p), "Both outputs active at the same time"); |
| 232 | + } |
| 233 | +} |
0 commit comments