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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ name = "nucleo-g474_hrtim"
harness = false
required-features = ["stm32g474", "defmt", "hrtim"]

[[test]]
name = "nucleo-g474_w_jumpers-hrtim"
harness = false

[lib]
test = false

Expand Down
7 changes: 7 additions & 0 deletions src/adc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,13 @@ impl<ADC: Instance> Adc<ADC, Configured> {
}
}

/// Releases the Adc as a DynamicAdc.
/// While this is not unsafe; using methods while the Adc is in the wrong state will mess it up.
#[inline(always)]
pub fn into_dynamic_adc(self) -> DynamicAdc<ADC> {
self.adc
}

/// Starts conversion sequence. Waits for the hardware to indicate it's actually started.
#[inline(always)]
pub fn start_conversion(mut self) -> Adc<ADC, Active> {
Expand Down
37 changes: 37 additions & 0 deletions src/hrtim/dac_trigger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use stm32_hrtim::DacResetOnCounterReset;
use stm32_hrtim::{
compare_register::HrCr2, output::HrOut1, timer::HrTim, DacResetOnOut1Set, DacResetTrigger,
DacStepOnCmp2, DacStepOnOut1Rst, DacStepTrigger,
};

use crate::dac::{IncTriggerSource, TriggerSource as RstTriggerSource};
use crate::stm32;

// TODO: use crate::stasis instead of references
macro_rules! impl_dac_triggers {
($($TIM:ident: $bits:expr),*) => {$(
unsafe impl<PSCL> IncTriggerSource for &HrCr2<stm32::$TIM, PSCL, DacStepOnCmp2> {
const BITS: u8 = $bits;
}
unsafe impl<PSCL, R: DacResetTrigger> IncTriggerSource for &HrOut1<stm32::$TIM, PSCL, R, DacStepOnOut1Rst> {
const BITS: u8 = $bits;
}

unsafe impl<PSCL, CPT1, CPT2> RstTriggerSource for &HrTim<stm32::$TIM, PSCL, CPT1, CPT2, DacResetOnCounterReset> {
const BITS: u8 = $bits;
}
unsafe impl<PSCL, S: DacStepTrigger> RstTriggerSource for &HrOut1<stm32::$TIM, PSCL, DacResetOnOut1Set, S> {
const BITS: u8 = $bits;
}
)*};
}

// RM0440 DAC1 interconnection
impl_dac_triggers! {
HRTIM_TIMA: 9,
HRTIM_TIMB: 10,
HRTIM_TIMC: 11,
HRTIM_TIMD: 12,
HRTIM_TIME: 13,
HRTIM_TIMF: 14
}
37 changes: 28 additions & 9 deletions src/hrtim/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod adc_trigger;
pub mod capture;
pub mod dac_trigger;
pub mod external_event;
pub mod fault;

Expand All @@ -13,7 +14,7 @@ use crate::{
use stm32_hrtim::{
control::{HrPwmControl, HrTimOngoingCalibration},
output::{HrOut1, HrOut2, ToHrOut},
HrParts, HrPwmBuilder,
DacResetTrigger, DacStepTrigger, HrParts, HrPwmBuilder,
};

pub use stm32_hrtim;
Expand All @@ -33,18 +34,32 @@ impl HrControltExt for crate::stm32::HRTIM_COMMON {
}
}

pub trait HrPwmBuilderExt<TIM, PSCL, PINS: ToHrOut<TIM>> {
fn finalize(self, control: &mut HrPwmControl) -> HrParts<TIM, PSCL, PINS::Out<PSCL>>;
pub trait HrPwmBuilderExt<TIM, PSCL, PINS, DacRst, DacStp>
where
PINS: ToHrOut<TIM, DacRst, DacStp>,
DacRst: DacResetTrigger,
DacStp: DacStepTrigger,
{
fn finalize(
self,
control: &mut HrPwmControl,
) -> HrParts<TIM, PSCL, PINS::Out<PSCL>, DacRst, DacStp>;
}

macro_rules! impl_finalize {
($($TIMX:ident),+) => {$(
impl<PSCL: stm32_hrtim::HrtimPrescaler, PINS: HrtimPin<$TIMX>> HrPwmBuilderExt<$TIMX, PSCL, PINS>
for HrPwmBuilder<$TIMX, PSCL, stm32_hrtim::PreloadSource, PINS>
impl<PSCL, PINS, DacRst, DacStp> HrPwmBuilderExt<$TIMX, PSCL, PINS, DacRst, DacStp>
for HrPwmBuilder<$TIMX, PSCL, stm32_hrtim::PreloadSource, PINS, DacRst, DacStp>
where
PSCL: stm32_hrtim::HrtimPrescaler,
PINS: HrtimPin<$TIMX> + ToHrOut<$TIMX, DacRst, DacStp>,
DacRst: DacResetTrigger,
DacStp: DacStepTrigger
{
fn finalize(
self,
control: &mut HrPwmControl,
) -> HrParts<$TIMX, PSCL, <PINS as ToHrOut<$TIMX>>::Out<PSCL>> {
) -> HrParts<$TIMX, PSCL, <PINS as ToHrOut<$TIMX, DacRst, DacStp>>::Out<PSCL>, DacRst, DacStp> {
let pins = self._init(control);
pins.connect_to_hrtim();
unsafe { MaybeUninit::uninit().assume_init() }
Expand Down Expand Up @@ -93,11 +108,15 @@ macro_rules! pins_helper {
($TIMX:ty, $HrOutY:ident, $CHY:ident<$CHY_AF:literal>) => {
//impl sealed::Sealed<$TIMX> for $CHY<GpioInputMode> {}

unsafe impl ToHrOut<$TIMX> for $CHY<gpio::DefaultMode> {
type Out<PSCL> = $HrOutY<$TIMX, PSCL>;
unsafe impl<DacRst, DacStp> ToHrOut<$TIMX, DacRst, DacStp> for $CHY
where
DacRst: DacResetTrigger,
DacStp: DacStepTrigger,
{
type Out<PSCL> = $HrOutY<$TIMX, PSCL, DacRst, DacStp>;
}

impl HrtimPin<$TIMX> for $CHY<gpio::DefaultMode> {
impl HrtimPin<$TIMX> for $CHY {
// Pin<Gpio, Index, Alternate<PushPull, AF>>
fn connect_to_hrtim(self) {
let _: $CHY<gpio::Alternate<{ $CHY_AF }>> = self.into_alternate();
Expand Down
163 changes: 140 additions & 23 deletions tests/nucleo-g474_hrtim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ mod common;

use common::test_pwm;
use fugit::{ExtU32, HertzU32, MicrosDurationU32};
use stm32_hrtim::compare_register::HrCompareRegister;
use stm32_hrtim::control::HrPwmControl;
use stm32_hrtim::deadtime::DeadtimeConfig;
use stm32_hrtim::output::HrOutput;
use stm32_hrtim::timer::HrTimer;
use stm32_hrtim::{HrParts, HrPwmAdvExt as _, Pscl64};
use stm32g4xx_hal::delay::SYSTDelayExt;
use stm32g4xx_hal::gpio::{GpioExt, PinExt};
use stm32g4xx_hal::hrtim::HrControltExt;
use stm32g4xx_hal::pwr::PwrExt;
use stm32g4xx_hal::rcc::{self, Rcc, RccExt};
use stm32_hrtim::{
compare_register::HrCompareRegister,
control::HrPwmControl,
deadtime::DeadtimeConfig,
output::{HrOutput, ToHrOut},
timer::HrTimer,
DacResetOnCounterReset, DacStepOnCmp2, HrParts, HrPwmAdvExt as _, Pscl64,
};
use stm32g4xx_hal::{
delay::SYSTDelayExt,
gpio::{GpioExt, PinExt},
hrtim::{HrControltExt, HrtimPin},
pwr::PwrExt,
rcc::{self, Rcc, RccExt},
};

use hal::stm32;
use stm32g4xx_hal as hal;
Expand All @@ -35,6 +39,10 @@ type Timer = crate::common::Timer<CYCLES_PER_US>;
#[embedded_test::tests]
mod tests {
use embedded_hal::delay::DelayNs;
use stm32g4xx_hal::{
adc::{self, temperature::Temperature, AdcClaim, AdcCommonExt},
dac::{self, DacExt, DacOut, SawtoothConfig},
};

#[test]
fn simple() {
Expand Down Expand Up @@ -63,7 +71,7 @@ mod tests {
..
},
mut hr_control,
) = setup(pin, None, dp.HRTIM_TIMA, dp.HRTIM_COMMON, &mut rcc);
) = setup_hrtim(pin, None, dp.HRTIM_TIMA, dp.HRTIM_COMMON, &mut rcc);

cr1.set_duty(PERIOD / 2);
out.enable_rst_event(&cr1); // Set low on compare match with cr1
Expand Down Expand Up @@ -99,6 +107,104 @@ mod tests {

deadtime_test(1, 68);
}

/// Test adc trigger and DAC trigger
///
/// Setup hrtim with DAC to generate a Sawtooth waveform. Use a compare register to accurately
/// place the measurement point.
///
/// | /| /| /|
/// | * / | / | / |
/// | * / | / | / |
/// | X | / | / |
/// | / * | / | / |
/// |/ * |/ |/ |
/// |--->*
/// cr3 indicates where in waveform to trigger ADC
///
#[test]
fn adc_trigger() {
use super::*;

let mut cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();

// Set system frequency to 16MHz * 15/1/2 = 120MHz
// This would lead to HrTim running at 120MHz * 32 = 3.84...
let mut rcc = setup_rcc_120MHz(dp.PWR, dp.RCC);
assert_eq!(rcc.clocks.sys_clk, F_SYS);
let mut delay = cp.SYST.delay(&rcc.clocks);

let gpioa = dp.GPIOA.split(&mut rcc);
let pa1 = gpioa.pa1.into_analog();
let pa4 = gpioa.pa4;
let pin = gpioa.pa8;

let (mut hrtimer, mut hr_control) =
setup_hrtim(pin, None, dp.HRTIM_TIMA, dp.HRTIM_COMMON, &mut rcc);

hrtimer.cr1.set_duty(PERIOD / 2);
hrtimer.out.enable_rst_event(&hrtimer.cr1); // Set low on compare match with cr1
hrtimer.out.enable_set_event(&hrtimer.timer); // Set high at new period

let dac_step_per_trigger = 16;
let cr2_ticks_per_dac_trigger = dac_step_per_trigger;
hrtimer.cr2.set_duty(cr2_ticks_per_dac_trigger);
let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc);
let mut ref_dac = dac1ch1.enable_sawtooth_generator(
SawtoothConfig::with_slope(dac::CountingDirection::Decrement, dac_step_per_trigger)
.inc_trigger(&hrtimer.cr2)
.reset_trigger(&hrtimer.timer),
&mut rcc,
);

hr_control.adc_trigger1.enable_source(&hrtimer.cr3);

defmt::debug!("Setup Adc1");
let mut adc12_common = dp
.ADC12_COMMON
.claim(adc::config::ClockMode::AdcHclkDiv4, &mut rcc);
let mut adc = adc12_common.claim(dp.ADC1, &mut delay);

adc.set_external_trigger((
adc::config::TriggerMode::RisingEdge,
(&hr_control.adc_trigger1).into(),
));
adc12_common.enable_temperature();
adc.set_continuous(hal::adc::config::Continuous::Discontinuous);
adc.reset_sequence();
adc.configure_channel(
&pa1,
adc::config::Sequence::One,
adc::config::SampleTime::Cycles_12_5,
);
let mut adc = adc.enable().into_dynamic_adc();
hrtimer.timer.start(&mut hr_control.control);

ref_dac.set_value(4095);

defmt::debug!("Awaiting first dac rst trigger");
while ref_dac.get_value() == 0 {}

//out.enable();

adc.start_conversion();

for _ in 0..2 {
let margin = 256;
for sampling_point in (margin..(PERIOD - margin)).step_by(16) {
hrtimer.cr3.set_duty(sampling_point); // Set sampling point
let sampling_point_as_12bit = sampling_point >> 4;

// Dac generates a sawtooth with negative slope which starts at 4095 every new period
let expected_dac_value = 4095 - sampling_point_as_12bit;
adc.wait_for_conversion_sequence();
let adc_value = adc.current_sample();
let diff = adc_value.abs_diff(expected_dac_value);
assert!(diff < 20);
}
}
}
}

#[allow(non_snake_case)]
Expand All @@ -120,16 +226,26 @@ fn setup_rcc_120MHz(pwr: stm32::PWR, rcc: stm32::RCC) -> Rcc {
)
}

fn setup<P: stm32g4xx_hal::hrtim::HrtimPin<stm32::HRTIM_TIMA>>(
fn setup_hrtim<P>(
pins: P,
deadtime_cfg: Option<DeadtimeConfig>,
hrtim_tima: stm32::HRTIM_TIMA,
hrtim_common: stm32::HRTIM_COMMON,
rcc: &mut Rcc,
) -> (
HrParts<stm32::HRTIM_TIMA, Pscl64, P::Out<Pscl64>>,
HrParts<
stm32::HRTIM_TIMA,
Pscl64,
<P as ToHrOut<stm32::HRTIM_TIMA, DacResetOnCounterReset, DacStepOnCmp2>>::Out<Pscl64>,
DacResetOnCounterReset,
DacStepOnCmp2,
>,
HrPwmControl,
) {
)
where
P: HrtimPin<stm32::HRTIM_TIMA>
+ ToHrOut<stm32::HRTIM_TIMA, DacResetOnCounterReset, DacStepOnCmp2>,
{
use stm32g4xx_hal::hrtim::HrPwmBuilderExt;
let (hr_control, ..) = hrtim_common.hr_control(rcc).wait_for_calibration();
let mut hr_control = hr_control.constrain();
Expand All @@ -138,19 +254,20 @@ fn setup<P: stm32g4xx_hal::hrtim::HrtimPin<stm32::HRTIM_TIMA>>(
// With a period of 60_000 set, this would be 60MHz/60_000 = 1kHz
let prescaler = Pscl64;

let mut hr_tim_builder: stm32_hrtim::HrPwmBuilder<
stm32::HRTIM_TIMA,
Pscl64,
stm32_hrtim::PreloadSource,
P,
> = hrtim_tima
let mut hr_tim_builder = hrtim_tima
.pwm_advanced(pins)
.prescaler(prescaler)
.period(PERIOD);

if let Some(dt) = deadtime_cfg {
hr_tim_builder = hr_tim_builder.deadtime(dt);
}
(hr_tim_builder.finalize(&mut hr_control), hr_control)
(
hr_tim_builder
.dac_trigger_cfg(DacResetOnCounterReset, DacStepOnCmp2)
.finalize(&mut hr_control),
hr_control,
)
}

fn deadtime_test(deadtime_rising_us: u32, deadtime_falling_us: u32) {
Expand Down Expand Up @@ -189,7 +306,7 @@ fn deadtime_test(deadtime_rising_us: u32, deadtime_falling_us: u32) {
..
},
mut hr_control,
) = setup(
) = setup_hrtim(
(pin, complementary_pin),
Some(deadtime_cfg),
dp.HRTIM_TIMA,
Expand Down
Loading
Loading