Skip to content

Commit 239880d

Browse files
committed
Add tests for hrtim
1 parent a725227 commit 239880d

File tree

4 files changed

+290
-3
lines changed

4 files changed

+290
-3
lines changed

Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ optional = true
5656
[dev-dependencies]
5757
cortex-m-rt = "0.7.2"
5858
defmt-rtt = "0.4.0"
59-
embedded-test = "0.6.1"
59+
embedded-test = "0.6.2"
6060
cortex-m-rtic = "1.1.4"
6161
cortex-m-semihosting = "0.3.5"
6262
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
@@ -148,7 +148,12 @@ required-features = ["stm32g474", "defmt", "cordic"]
148148
[[test]]
149149
name = "nucleo-g474_w_jumpers"
150150
harness = false
151-
required-features = ["stm32g474", "defmt", "cordic"]
151+
required-features = ["stm32g474", "defmt"]
152+
153+
[[test]]
154+
name = "nucleo-g474_hrtim"
155+
harness = false
156+
required-features = ["stm32g474", "defmt"]
152157

153158
[lib]
154159
test = false

src/hrtim/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ use gpio::{
7676

7777
use gpio::gpioc::{PC6, PC7};
7878

79-
trait HrtimPin<TIM>: ToHrOut<TIM> {
79+
pub trait HrtimPin<TIM>: ToHrOut<TIM> {
8080
fn connect_to_hrtim(self);
8181
}
8282

tests/common/mod.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,52 @@ pub fn await_p<const CYCLES_PER_US: u32>(
6565
}
6666
}
6767
}
68+
69+
#[allow(dead_code)]
70+
pub fn test_pwm<const CYCLES_PER_US: u32>(timer: &Timer<CYCLES_PER_US>, pin_num: u8, t_lo: MicrosDurationU32, t_hi: MicrosDurationU32, t_max_deviation: MicrosDurationU32, first_start_mult: u32) {
71+
defmt::debug!("Awaiting first rising edge...");
72+
73+
let t_lo_min = t_lo - t_max_deviation;
74+
let t_lo_max = t_lo + t_max_deviation;
75+
let t_hi_min = t_hi - t_max_deviation;
76+
let t_hi_max = t_hi + t_max_deviation;
77+
78+
let mut hi_duration = 0u32.micros();
79+
let mut lo_duration = 0.micros();
80+
81+
let duration_until_lo = await_lo(timer, pin_num, t_hi_max).unwrap();
82+
let first_lo_duration = await_hi(timer, pin_num, t_lo_max * first_start_mult).unwrap(); // Some extra time until started
83+
84+
for _ in 0..100 {
85+
// Make sure the timer half periods are within 495-505us
86+
hi_duration = await_lo(timer, pin_num, t_hi_max).unwrap();
87+
//defmt::debug!("hi_duration: {}", hi_duration);
88+
defmt::assert!(
89+
hi_duration > t_hi_min && hi_duration < t_hi_max,
90+
"hi: {} < {} < {}",
91+
t_hi_min,
92+
hi_duration,
93+
t_hi_max
94+
);
95+
96+
lo_duration = await_hi(timer, pin_num, t_lo_max).unwrap();
97+
//defmt::debug!("lo_duration: {}", lo_duration);
98+
defmt::assert!(
99+
lo_duration > t_lo_min && lo_duration < t_lo_max,
100+
"lo: {} < {} < {}",
101+
t_lo_min,
102+
lo_duration,
103+
t_lo_max
104+
);
105+
//defmt::debug!("");
106+
}
107+
108+
// Prints deferred until here to not mess up timing
109+
defmt::debug!("Waited ~{} until low", duration_until_lo);
110+
defmt::debug!("First low half period: {}", first_lo_duration);
111+
112+
defmt::debug!("High half period: {}", hi_duration);
113+
defmt::debug!("Low half period: {}", lo_duration);
114+
115+
defmt::debug!("Done!");
116+
}

tests/nucleo-g474_hrtim.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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

Comments
 (0)