diff --git a/.vscode/settings.json b/.vscode/settings.json index 5ecc8746..ace3bbcf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,11 +2,13 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.check.extraArgs": [ "--examples", + "--bins", ], "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", "rust-analyzer.cargo.features": [ "stm32g484", "defmt", - "cordic" + "cordic", + "hrtim" ] } diff --git a/Cargo.toml b/Cargo.toml index 05da015b..0aaa260c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ fugit = "0.3.7" stm32-usbd = { version = "0.7.0", optional = true } fixed = { version = "1.28.0", optional = true } embedded-io = "0.6" +stm32-hrtim = { git = "https://github.com/usbalbin/stm32-hrtim", rev = "ce1da43264a59b932eaf01651eba20b9cd0d96ba", optional = true } [dependencies.cortex-m] version = "0.7.7" @@ -81,9 +82,9 @@ usb = ["dep:stm32-usbd"] stm32g431 = ["stm32g4/stm32g431", "cat2"] stm32g441 = ["stm32g4/stm32g441", "cat2"] stm32g473 = ["stm32g4/stm32g473", "cat3", "adc3", "adc4", "adc5"] -stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5"] +stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5", "hrtim", "stm32-hrtim/stm32g474"] stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"] -stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5"] +stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5", "hrtim", "stm32-hrtim/stm32g484"] stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"] stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"] log-itm = ["cortex-m-log/itm"] @@ -95,6 +96,7 @@ defmt = [ "nb/defmt-0-3", "embedded-hal/defmt-03", "embedded-io/defmt-03", + "stm32-hrtim?/defmt" ] cordic = ["dep:fixed"] adc3 = [] @@ -107,6 +109,7 @@ cat3 = [] cat4 = [] can = ["dep:fdcan"] +hrtim = ["dep:stm32-hrtim"] [profile.dev] codegen-units = 1 @@ -127,6 +130,17 @@ codegen-units = 1 incremental = false lto = true +[[test]] +name = "nucleo-g474" +harness = false + +[[test]] +name = "nucleo-g474_w_jumpers" +harness = false + +[lib] +test = false + [[example]] name = "can-echo" required-features = ["can"] @@ -143,13 +157,47 @@ required-features = ["usb"] name = "cordic" required-features = ["cordic"] -[[test]] -name = "nucleo-g474" -harness = false +[[example]] +name = "hrtim-adc-trigger" +required-features = ["hrtim"] +path = "examples/hrtim/adc-trigger.rs" -[[test]] -name = "nucleo-g474_w_jumpers" -harness = false +[[example]] +name = "hrtim-capture" +required-features = ["hrtim"] +path = "examples/hrtim/capture.rs" -[lib] -test = false +[[example]] +name = "hrtim-capture-dma" +required-features = ["hrtim"] +path = "examples/hrtim/capture-dma.rs" + +[[example]] +name = "hrtim-eev-comp" +required-features = ["hrtim"] +path = "examples/hrtim/eev-comp.rs" + +[[example]] +name = "hrtim-eev" +required-features = ["hrtim"] +path = "examples/hrtim/eev.rs" + +[[example]] +name = "hrtim-flt-comp" +required-features = ["hrtim"] +path = "examples/hrtim/flt-comp.rs" + +[[example]] +name = "hrtim-flt" +required-features = ["hrtim"] +path = "examples/hrtim/flt.rs" + +[[example]] +name = "hrtim" +required-features = ["hrtim"] +path = "examples/hrtim/hrtim.rs" + +[[example]] +name = "hrtim-master" +required-features = ["hrtim"] +path = "examples/hrtim/master.rs" diff --git a/examples/hrtim/adc-trigger.rs b/examples/hrtim/adc-trigger.rs new file mode 100644 index 00000000..a6746cea --- /dev/null +++ b/examples/hrtim/adc-trigger.rs @@ -0,0 +1,162 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral to trigger the ADC at various points of the switch cycle off HRTIM_TIMA +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, output::HrOutput, timer::HrTimer, HrParts, HrPwmAdvExt, + Pscl4, +}; +use stm32g4xx_hal::{ + adc::{self, temperature::Temperature, AdcClaim, AdcCommonExt, Vref}, + delay::SYSTDelayExt, + dma::{self, channel::DMAExt, config::DmaConfig, TransferExt}, + gpio::GpioExt, + hrtim::{HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, +}; + +#[entry] +fn main() -> ! { + const VREF: f32 = 3.3; + + info!("start"); + + let dp = Peripherals::take().unwrap(); + let cp = CorePeripherals::take().expect("cannot take core peripherals"); + + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + info!("rcc"); + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let dma::channel::Channels { ch1: dma1ch1, .. } = dp.DMA1.split(&rcc); + let config = DmaConfig::default() + .transfer_complete_interrupt(true) + .circular_buffer(true) + .memory_increment(true); + + info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let pa0 = gpioa.pa0.into_analog(); + + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + // . ^ ^ + // . | | + //AD samlp pa0 temp + let period = 0xFFFF; + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut cr3, + mut cr4, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + + cr1.set_duty(period / 2); + cr3.set_duty(period / 3); + cr4.set_duty((2 * u32::from(period) / 3) as u16); + + hr_control.adc_trigger1.enable_source(&cr3); + hr_control.adc_trigger1.enable_source(&cr4); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out2.enable_rst_event(&cr1); + + out1.enable_set_event(&timer); // Set high at new period + out2.enable_set_event(&timer); + + info!("Setup Adc1"); + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &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(adc::config::Continuous::Discontinuous); + adc.reset_sequence(); + adc.configure_channel( + &pa0, + adc::config::Sequence::One, + adc::config::SampleTime::Cycles_640_5, + ); + adc.configure_channel( + &Temperature, + adc::config::Sequence::Two, + adc::config::SampleTime::Cycles_640_5, + ); + + info!("Setup DMA"); + let first_buffer = cortex_m::singleton!(: [u16; 10] = [0; 10]).unwrap(); + + let mut transfer = dma1ch1.into_circ_peripheral_to_memory_transfer( + adc.enable_dma(adc::config::Dma::Continuous), + &mut first_buffer[..], + config, + ); + + transfer.start(|adc| adc.start_conversion()); + + out1.enable(); + out2.enable(); + + timer.start(&mut hr_control.control); + + loop { + let mut b = [0_u16; 4]; + let r = transfer.read_exact(&mut b); + + info!("read: {}", r); + assert!(r == b.len()); + + let millivolts = Vref::sample_to_millivolts((b[0] + b[2]) / 2); + info!("pa3: {}mV", millivolts); + let temp = Temperature::temperature_to_degrees_centigrade( + (b[1] + b[3]) / 2, + VREF, + adc::config::Resolution::Twelve, + ); + info!("temp: {}℃C", temp); + } +} diff --git a/examples/hrtim/capture-dma.rs b/examples/hrtim/capture-dma.rs new file mode 100644 index 00000000..126853d6 --- /dev/null +++ b/examples/hrtim/capture-dma.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral's capture function to detect phase shift between a digital event and the output of HRTIM_TIMA +use cortex_m_rt::entry; +use stm32_hrtim::{ + capture, + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::{HrSlaveTimerCpt, HrTimer, TimerSplitCapture}, + HrParts, HrPwmAdvExt, Pscl128, +}; +use stm32g4xx_hal::{ + dma::{channel::DMAExt, config::DmaConfig, TransferExt}, + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + info!("start"); + + let dp = Peripherals::take().unwrap(); + + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + info!("rcc"); + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + // PA8 (D7 on Nucleo G474RE) + let pin_a = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // t1 t2 . + // | | . + // v v . + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Both, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + timer, + mut cr1, + out: mut out1, + dma_channel, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(period / 2); + + let TimerSplitCapture { + mut timer, + ch1: mut capture, + .. + } = timer.split_capture(); + timer.start(&mut hr_control.control); + out1.enable(); + + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + info!("Setup DMA"); + let channels = dp.DMA1.split(&rcc); + let config = DmaConfig::default() + .transfer_complete_interrupt(false) + .circular_buffer(true) + .memory_increment(true); + + let first_buffer = cortex_m::singleton!(: [u32; 16] = [0; 16]).unwrap(); + let mut transfer = channels.ch1.into_circ_peripheral_to_memory_transfer( + capture.enable_dma(dma_channel), + &mut first_buffer[..], + config, + ); + + transfer.start(|_| ()); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + let mut data = [0; 2]; + transfer.read_exact(&mut data); + let [t1, t2] = data.map(|x| capture::dma_value_to_signed(x, period)); + cr1.set_duty(duty as u16); + info!("Capture: t1: {}, t2: {}, duty: {}, ", t1, t2, old_duty); + old_duty = duty; + } + } +} diff --git a/examples/hrtim/capture.rs b/examples/hrtim/capture.rs new file mode 100644 index 00000000..1bb536ea --- /dev/null +++ b/examples/hrtim/capture.rs @@ -0,0 +1,119 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral's capture function to detect phase shift between a digital event and the output of HRTIM_TIMA +use cortex_m_rt::entry; +use stm32_hrtim::{ + capture::HrCapture, + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::{HrSlaveTimerCpt, HrTimer}, + HrParts, HrPwmAdvExt, Pscl128, +}; +use stm32g4xx_hal::{ + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + info!("start"); + + let dp = Peripherals::take().unwrap(); + + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + info!("rcc"); + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + // PA8 (D7 on Nucleo G474RE) + let pin_a = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Falling, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + + cr1.set_duty(period / 2); + timer.start(&mut hr_control.control); + out.enable(); + + let capture = timer.capture_ch1(); + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + if let Some(value) = capture.get_signed(period) { + info!( + "Capture: {:?}, duty: {}, diff: {}", + value, + old_duty, + value - old_duty as i32 + ); + cr1.set_duty(duty as u16); + old_duty = duty; + } + } + } +} diff --git a/examples/hrtim/eev-comp.rs b/examples/hrtim/eev-comp.rs new file mode 100644 index 00000000..68aeedad --- /dev/null +++ b/examples/hrtim/eev-comp.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral together with a comparator to implement a cycle by cycle current limit. +/// Once the comparator input exceeds the reference set by the DAC, the output is set low thus limiting the pulse width and in turn the current. +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::HrTimer, + timer_eev_cfg::{EevCfg, EevCfgs}, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + comparator::{self, ComparatorExt, ComparatorSplit}, + dac::{self, DacExt, DacOut}, + delay::SYSTDelayExt, + gpio::{GpioExt, SignalEdge}, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let exti = dp.EXTI; + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let gpioa = dp.GPIOA.split(&mut rcc); + + let input = gpioa.pa1.into_analog(); + let pin_a = gpioa.pa8; + + let dac1ch1 = dp.DAC1.constrain(dac::Dac1IntSig1, &mut rcc); + let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc); + + // Use dac to define the fault threshold + // 2^12 / 2 = 2^11 for about half of VCC + let limit = 1 << 11; + dac.set_value(limit); + + let (comp1, ..) = dp.COMP.split(&mut rcc); + + let comp1 = comp1.comparator( + input, + dac, + comparator::Config::default().hysteresis(comparator::Hysteresis::None), + //.output_inverted(), + &rcc.clocks, + ); + comp1.listen(SignalEdge::Rising, &exti); + let comp1 = comp1.enable().lock(); + + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input4 = eev_inputs + .eev_input4 + .bind(&comp1) + .edge_or_polarity(external_event::EdgeOrPolarity::Polarity( + Polarity::ActiveHigh, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + // . . * . + // . 33% . * . . . + // .-----. .--* . .-----. .----- + //out1 | | | | . | | | + // | | | * . | | | + // ------ ----------- ------------------------------ ----------- + // . . * . . . + // . . * . . . + // . . *-------------* . . + //eev . . | .| . . + // . . | .| . . + // ------------------------- .-------------------------------------- + // . . * . . . + // . . * . . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default().eev4(EevCfg::default())) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input4); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + info!( + "Comp: {}, pending: {}", + comp1.output(), + comp1.is_pending(&exti) + ); + } +} diff --git a/examples/hrtim/eev.rs b/examples/hrtim/eev.rs new file mode 100644 index 00000000..1367e2f3 --- /dev/null +++ b/examples/hrtim/eev.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral together with a digital input to implement a cycle by cycle current limit. +/// Once the digital input goes high, the output is set low thus limiting the pulse width and in turn the current. +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::HrTimer, + timer_eev_cfg::EevCfgs, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input3 = eev_inputs + .eev_input3 + .bind(gpiob.pb7.into_pull_down_input()) + .edge_or_polarity(external_event::EdgeOrPolarity::Polarity( + Polarity::ActiveHigh, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . * . + // . 33% . * . . . + // .-----. .--* .-----. .-----. .----- + //out1 | | | | | | | | | + // | | | * | | | | | + // ------ ----------- -------------- ----------- ----------- + // . . * . . . + // . . * . . . + // . . *--------* . . . + //eev . . | | . . . + // . . | | . . . + // ------------------------- ------------------------------------------ + // . . * . . . + // . . * . . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default()) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input3); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + cortex_m::asm::nop() + } +} diff --git a/examples/hrtim/flt-comp.rs b/examples/hrtim/flt-comp.rs new file mode 100644 index 00000000..579d5061 --- /dev/null +++ b/examples/hrtim/flt-comp.rs @@ -0,0 +1,152 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral together with a comparator to implement a current fault. +/// Once the comparator input exceeds the reference set by the DAC, the output is forced low and put into a fault state. +use cortex_m_rt::entry; +use fugit::ExtU32 as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + fault::{FaultAction, FaultMonitor}, + output::HrOutput, + timer::HrTimer, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + self as hal, + adc::{AdcClaim, AdcCommonExt}, + comparator::{self, ComparatorExt, ComparatorSplit}, + dac::{Dac3IntSig1, DacExt, DacOut}, + delay::{DelayExt as _, SYSTDelayExt}, + gpio::GpioExt, + hrtim::{fault::FaultInput, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stasis::Freeze, + stm32::{CorePeripherals, Peripherals}, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84GHz... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + let adc12_common = dp + .ADC12_COMMON + .claim(hal::adc::config::ClockMode::AdcHclkDiv4, &mut rcc); + let mut adc1 = adc12_common.claim_and_configure(dp.ADC1, Default::default(), &mut delay); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpioc = dp.GPIOC.split(&mut rcc); + + let dac3ch1 = dp.DAC3.constrain(Dac3IntSig1, &mut rcc); + let mut dac = dac3ch1.enable(&mut rcc); + + // Use dac to define the fault threshold + // 2^12 / 2 = 2^11 for about half of VCC + let fault_limit = 60; + dac.set_value(fault_limit); + + let (_comp1, _comp2, comp3, ..) = dp.COMP.split(&mut rcc); + + let (pc1, [pc1_token]) = gpioc.pc1.into_analog().freeze(); + let comp3 = comp3 + .comparator( + pc1_token, + dac, + comparator::Config::default() + .hysteresis(comparator::Hysteresis::None) + .output_inverted(), + &rcc.clocks, + ) + .enable(); + + let (hr_control, flt_inputs, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let fault_source5 = flt_inputs + .fault_input5 + .bind(comp3) + .polarity(Polarity::ActiveHigh) + .finalize(&mut hr_control); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + out: mut out1, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(0xFFFF) + .with_fault_source(fault_source5) // Set fault source + .fault_action1(FaultAction::ForceInactive) + .fault_action2(FaultAction::ForceInactive) + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out1.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + info!( + "State: {:?}, comp: {}, is_fault_active: _, pc1: {}", + out1.get_state(), + //comp3.output(), TODO + hr_control.fault_5.is_fault_active(), + adc1.convert(&pc1, hal::adc::config::SampleTime::Cycles_92_5) + ); + } + if hr_control.fault_5.is_fault_active() { + hr_control.fault_5.clear_fault(); // Clear fault every 5s + out1.enable(); + info!("failt cleared, and output reenabled"); + } + } +} diff --git a/examples/hrtim/flt.rs b/examples/hrtim/flt.rs new file mode 100644 index 00000000..6ed98bc6 --- /dev/null +++ b/examples/hrtim/flt.rs @@ -0,0 +1,115 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +/// Example showcasing the use of the HRTIM peripheral together with a comparator to implement a current fault. +/// Once the digital input goes high, the output is forced low and put into a fault state. +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + fault::{FaultAction, FaultMonitor}, + output::HrOutput, + timer::HrTimer, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, + gpio::GpioExt, + hrtim::{fault::FaultInput, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, + time::ExtU32, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + let (hr_control, flt_inputs, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let pb10 = gpiob.pb10.into_pull_down_input().into_alternate(); + let fault_source3 = flt_inputs + .fault_input3 + .bind(pb10) + .polarity(Polarity::ActiveHigh) + .finalize(&mut hr_control); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(0xFFFF) + .with_fault_source(fault_source3) + .fault_action1(FaultAction::ForceInactive) + .fault_action2(FaultAction::ForceInactive) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + info!("State: {:?}", out.get_state()); + } + if hr_control.fault_3.is_fault_active() { + hr_control.fault_3.clear_fault(); // Clear fault every 5s + out.enable(); + info!("failt cleared, and output reenabled"); + } + } +} diff --git a/examples/hrtim/hrtim.rs b/examples/hrtim/hrtim.rs new file mode 100644 index 00000000..259ed0d6 --- /dev/null +++ b/examples/hrtim/hrtim.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, output::HrOutput, timer::HrTimer, HrParts, HrPwmAdvExt, + Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, + gpio::GpioExt, + hrtim::{HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, + time::ExtU32, +}; + +#[entry] +fn main() -> ! { + info!("Initializing..."); + + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 14.6kHz... + let prescaler = Pscl4; + + let gpioa = dp.GPIOA.split(&mut rcc); + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .period(0xFFFF) + .push_pull_mode(true) // Set push pull mode, out1 and out2 are + // alternated every period with one being + // inactive and the other getting to output its wave form + // as normal + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out2.enable_rst_event(&cr1); + + out1.enable_set_event(&timer); // Set high at new period + out2.enable_set_event(&timer); + + out1.enable(); + out2.enable(); + timer.start(&mut hr_control.control); + + loop { + // Step frequency from 14.6kHz to about 146kHz(half of that when only looking at one pin) + for i in 1..=10 { + let new_period = u16::MAX / i; + + cr1.set_duty(new_period / 3); + timer.set_period(new_period); + + delay.delay(500_u32.millis()); + } + } +} diff --git a/examples/hrtim/master.rs b/examples/hrtim/master.rs new file mode 100644 index 00000000..6e6ed81c --- /dev/null +++ b/examples/hrtim/master.rs @@ -0,0 +1,136 @@ +#![no_std] +#![no_main] + +#[path = "../utils/mod.rs"] +mod utils; +use utils::logger::info; + +use cortex_m_rt::entry; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + output::HrOutput, + timer::{HrSlaveTimer, HrTimer}, + HrParts, HrPwmAdvExt, HrTimerMode, MasterPreloadSource, PreloadSource, Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, + gpio::GpioExt, + hrtim::{HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, + time::ExtU32, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + let gpioa = dp.GPIOA.split(&mut rcc); + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .push_pull_mode(true) // Set push pull mode, out1 and out2 are + // alternated every period with one being + // inactive and the other getting to output its wave form + // as normal + .preload(PreloadSource::OnMasterTimerUpdate) + .timer_mode(HrTimerMode::SingleShotRetriggerable) + .finalize(&mut hr_control); + + let HrParts { + timer: mut mtimer, + cr1: mut mcr1, + .. + } = dp + .HRTIM_MASTER + .pwm_advanced(()) + .prescaler(prescaler) + .preload(MasterPreloadSource::OnMasterRepetitionUpdate) + .period(0xFFFF) + .finalize(&mut hr_control); + + // Run in sync with master timer + timer.enable_reset_event(&mtimer); + + out1.enable_rst_event(&mcr1); // Set low on compare match with cr1 + out2.enable_rst_event(&mcr1); + + out1.enable_set_event(&mtimer); // Set high at new period + out2.enable_set_event(&mtimer); + + out1.enable(); + out2.enable(); + + mtimer.start(&mut hr_control.control); + timer.start(&mut hr_control.control); + + info!("Running"); + + loop { + // Step frequency from 15kHz to about 146kHz(half of that when only looking at one pin) + for i in 1..=10 { + let new_period = u16::MAX / i; + + mcr1.set_duty(new_period / 3); + cr1.set_duty(new_period / 3 - 1000); + mtimer.set_period(new_period); + timer.set_period(new_period - 1000); + + info!( + "period: {}, duty: {}, get_duty: {}, get_period: {}", + new_period, + new_period / 3, + mcr1.get_duty(), + mtimer.get_period() + ); + + delay.delay(500_u32.millis()); + } + } +} diff --git a/src/hrtim/adc_trigger.rs b/src/hrtim/adc_trigger.rs new file mode 100644 index 00000000..f79aae0c --- /dev/null +++ b/src/hrtim/adc_trigger.rs @@ -0,0 +1,53 @@ +use crate::adc; +use stm32_hrtim::adc_trigger::{ + AdcTrigger1, AdcTrigger10, AdcTrigger2, AdcTrigger3, AdcTrigger4, AdcTrigger5, AdcTrigger6, + AdcTrigger7, AdcTrigger8, AdcTrigger9, +}; + +macro_rules! impl_adc1234_trigger { + ($($t:ident: [$variant345:ident $(, $variant12:ident)*]),*) => {$( + $(impl From<&$t> for adc::config::ExternalTrigger12 { + fn from(_val: &$t) -> Self { + adc::config::ExternalTrigger12::$variant12 + } + })* + + impl From<&$t> for adc::config::ExternalTrigger345 { + fn from(_val: &$t) -> Self { + adc::config::ExternalTrigger345::$variant345 + } + } + )*} +} + +macro_rules! impl_adc5678910_trigger { + ($($t:ident: [$variant:ident]),*) => {$( + impl From<&$t> for adc::config::ExternalTrigger12 { + fn from(_val: &$t) -> Self { + adc::config::ExternalTrigger12::$variant + } + } + + impl From<&$t> for adc::config::ExternalTrigger345 { + fn from(_val: &$t) -> Self { + adc::config::ExternalTrigger345::$variant + } + } + )*} +} + +impl_adc1234_trigger! {//adc345, adc12 + AdcTrigger1: [Hrtim_adc_trg_1, Hrtim_adc_trg_1], + AdcTrigger2: [Hrtim_adc_trg_2], + AdcTrigger3: [Hrtim_adc_trg_3, Hrtim_adc_trg_3], + AdcTrigger4: [Hrtim_adc_trg_4] +} + +impl_adc5678910_trigger! { + AdcTrigger5: [Hrtim_adc_trg_5], + AdcTrigger6: [Hrtim_adc_trg_6], + AdcTrigger7: [Hrtim_adc_trg_7], + AdcTrigger8: [Hrtim_adc_trg_8], + AdcTrigger9: [Hrtim_adc_trg_9], + AdcTrigger10: [Hrtim_adc_trg_10] +} diff --git a/src/hrtim/capture.rs b/src/hrtim/capture.rs new file mode 100644 index 00000000..a774f313 --- /dev/null +++ b/src/hrtim/capture.rs @@ -0,0 +1,32 @@ +use stm32_hrtim::{ + capture::{Ch1, Ch2, Dma, HrCapt}, + pac::{HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF}, +}; + +use crate::dma::{mux::DmaMuxResources, traits::TargetAddress, PeripheralToMemory}; + +macro_rules! impl_dma_traits { + ($TIMX:ident, $CH:ident, $cptXr:ident) => { + unsafe impl TargetAddress for HrCapt<$TIMX, PSCL, $CH, Dma> { + #[inline(always)] + fn address(&self) -> u32 { + let tim = unsafe { &*$TIMX::ptr() }; + &tim.$cptXr() as *const _ as u32 + } + + type MemSize = u32; + + const REQUEST_LINE: Option = Some(DmaMuxResources::$TIMX as u8); + } + }; + ($($TIMX:ident),+) => { + $( + impl_dma_traits!($TIMX, Ch1, cpt1r); + impl_dma_traits!($TIMX, Ch2, cpt2r); + )+ + } +} + +impl_dma_traits! { + HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF +} diff --git a/src/hrtim/external_event.rs b/src/hrtim/external_event.rs new file mode 100644 index 00000000..a6f4c93f --- /dev/null +++ b/src/hrtim/external_event.rs @@ -0,0 +1,61 @@ +use stm32_hrtim::external_event::{EevInput, EevSrcBits, SourceBuilder}; + +use crate::gpio::{ + self, + gpiob::{PB3, PB4, PB5, PB6, PB7, PB8, PB9}, + gpioc::{PC11, PC12, PC5, PC6}, + AF13, AF3, +}; + +pub trait EevInputExt { + fn bind(self, src: SRC) -> SourceBuilder + where + SRC: EevSrcBits; +} + +macro_rules! impl_eev_input { + ($($N:literal: COMP=[$compX:ident $(, ($compY:ident, $compY_src_bits:literal))*], PINS=[$(($pin:ident, $af:ident)),*])*) => {$( + $(unsafe impl EevSrcBits<$N> for $pin>{ + const SRC_BITS: u8 = 0b00; + fn cfg(self) { + self.into_alternate::<$af>(); + } + })* + + unsafe impl EevSrcBits<$N> for &crate::comparator::Comparator + where ED: crate::comparator::EnabledState + { + const SRC_BITS: u8 = 0b01; + } + + $( + unsafe impl EevSrcBits<$N> for &crate::comparator::Comparator + where ED: crate::comparator::EnabledState + { + const SRC_BITS: u8 = $compY_src_bits; + } + )* + + impl EevInputExt<$N> for EevInput<$N> { + fn bind(self, src: SRC) -> SourceBuilder<$N, IS_FAST> + where SRC: EevSrcBits<$N> + { + src.cfg(); + unsafe { SourceBuilder::new(SRC::SRC_BITS) } + } + } + )*}; +} + +impl_eev_input! { + 1: COMP = [COMP2], PINS = [(PC12, AF3)] + 2: COMP = [COMP4], PINS = [(PC11, AF3)] + 3: COMP = [COMP6], PINS = [(PB7, AF13)] + 4: COMP = [COMP1, (COMP5, 0b10)], PINS = [(PB6, AF13)] + 5: COMP = [COMP3, (COMP7, 0b10)], PINS = [(PB9, AF13)] + 6: COMP = [COMP2, (COMP1, 0b10)], PINS = [(PB5, AF13)] + 7: COMP = [COMP4], PINS = [(PB4, AF13)] + 8: COMP = [COMP6, (COMP3, 0b10)], PINS = [(PB8, AF13)] + 9: COMP = [COMP5, (COMP4, 0b11)], PINS = [(PB3, AF13)] + 10: COMP = [COMP7], PINS = [(PC5, AF13), (PC6, AF3)] +} diff --git a/src/hrtim/fault.rs b/src/hrtim/fault.rs new file mode 100644 index 00000000..94ee7f25 --- /dev/null +++ b/src/hrtim/fault.rs @@ -0,0 +1,61 @@ +use stm32_hrtim::fault::{self, SourceBuilder}; + +use crate::{ + comparator::{COMP1, COMP2, COMP3, COMP4, COMP5, COMP6}, + gpio::{ + self, + gpioa::{PA12, PA15}, + gpiob::{PB0, PB10, PB11}, + gpioc::{PC10, PC7}, + AF13, AF3, + }, +}; + +// TODO: Come up with a better names + +pub trait FaultInput { + fn bind(self, src: S) -> SourceBuilder + where + Self: Sized; +} + +macro_rules! impl_faults { + ($( + $input:ident: + PINS=[($pin:ident, $af:ident) $(,($pin_b:ident, $af_b:ident))*], + COMP=$compX:ident, + )+) => {$( + impl FaultInput<$pin>> for fault::$input { + fn bind(self, _pin: $pin>) -> SourceBuilder { + unsafe { SourceBuilder::new(self, 0b00) } + } + } + + $( + impl FaultInput<$pin_b>> for fault::$input { + fn bind(self, _pin: $pin_b>) -> SourceBuilder { + unsafe { SourceBuilder::new(self, 0b00) } + } + } + )* + + impl FaultInput> for fault::$input { + fn bind(self, _comp: crate::comparator::Comparator<$compX, crate::comparator::Enabled>) -> SourceBuilder { + unsafe { SourceBuilder::new(self, 0b01) } + } + } + + /*pub fn bind_external(?) { + SourceBuilder::new(self, 0b10); + }*/ + )+} +} + +impl_faults!( + FaultInput1: PINS=[(PA12, AF13)], COMP=COMP2, + FaultInput2: PINS=[(PA15, AF13)], COMP=COMP4, + FaultInput3: PINS=[(PB10, AF13)], COMP=COMP6, + FaultInput4: PINS=[(PB11, AF13)], COMP=COMP1, + FaultInput5: PINS=[(PB0, AF13), (PC7, AF3)], COMP=COMP3, + FaultInput6: PINS=[(PC10, AF13)], COMP=COMP5, +); diff --git a/src/hrtim/mod.rs b/src/hrtim/mod.rs new file mode 100644 index 00000000..4db339c4 --- /dev/null +++ b/src/hrtim/mod.rs @@ -0,0 +1,126 @@ +pub mod adc_trigger; +pub mod capture; +pub mod external_event; +pub mod fault; + +use core::mem::MaybeUninit; + +use crate::{ + gpio, + rcc::{Enable, Reset}, + stm32::{ + self, HRTIM_COMMON, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF, + }, +}; +use stm32_hrtim::{ + control::{HrPwmControl, HrTimOngoingCalibration}, + output::{HrOut1, HrOut2, ToHrOut}, + HrParts, HrPwmBuilder, +}; + +pub use stm32_hrtim; + +pub trait HrControltExt { + fn hr_control(self, rcc: &mut crate::rcc::Rcc) -> HrTimOngoingCalibration; +} + +impl HrControltExt for crate::stm32::HRTIM_COMMON { + fn hr_control(self, _rcc: &mut crate::rcc::Rcc) -> HrTimOngoingCalibration { + let rcc = unsafe { &*stm32::RCC::ptr() }; + HRTIM_COMMON::enable(rcc); + HRTIM_COMMON::reset(rcc); + + // TODO: Verify that the HRTIM gets a clock of 100-170MHz as input + // SAFETY: We have enabled the rcc + unsafe { HrTimOngoingCalibration::hr_control() } + } +} + +pub trait HrPwmBuilderExt> { + fn finalize(self, control: &mut HrPwmControl) -> HrParts>; +} +macro_rules! impl_finalize { + ($($TIMX:ident),+) => {$( + impl> HrPwmBuilderExt<$TIMX, PSCL, PINS> + for HrPwmBuilder<$TIMX, PSCL, stm32_hrtim::PreloadSource, PINS> + { + fn finalize( + self, + control: &mut HrPwmControl, + ) -> HrParts<$TIMX, PSCL, >::Out> { + let pins = self._init(control); + pins.connect_to_hrtim(); + unsafe { MaybeUninit::uninit().assume_init() } + } + } + )+}; +} + +impl_finalize! { + HRTIM_TIMA, + HRTIM_TIMB, + HRTIM_TIMC, + HRTIM_TIMD, + HRTIM_TIME, + HRTIM_TIMF +} + +use gpio::{ + gpioa::{PA10, PA11, PA8, PA9}, + gpioc::PC8, +}; + +use gpio::{ + gpiob::{PB12, PB13, PB14, PB15}, + gpioc::PC9, +}; + +use gpio::gpioc::{PC6, PC7}; + +trait HrtimPin: ToHrOut { + fn connect_to_hrtim(self); +} + +impl HrtimPin for (PA, PB) +where + PA: HrtimPin, + PB: HrtimPin, +{ + fn connect_to_hrtim(self) { + self.0.connect_to_hrtim(); + self.1.connect_to_hrtim(); + } +} + +macro_rules! pins_helper { + ($TIMX:ty, $HrOutY:ident, $CHY:ident<$CHY_AF:literal>) => { + //impl sealed::Sealed<$TIMX> for $CHY {} + + unsafe impl ToHrOut<$TIMX> for $CHY { + type Out = $HrOutY<$TIMX, PSCL>; + } + + impl HrtimPin<$TIMX> for $CHY { + // Pin> + fn connect_to_hrtim(self) { + let _: $CHY> = self.into_alternate(); + } + } + }; +} + +macro_rules! pins { + ($($TIMX:ty: CH1: $CH1:ident<$CH1_AF:literal>, CH2: $CH2:ident<$CH2_AF:literal>),+) => {$( + pins_helper!($TIMX, HrOut1, $CH1<$CH1_AF>); + pins_helper!($TIMX, HrOut2, $CH2<$CH2_AF>); + )+}; +} + +pins! { + HRTIM_TIMA: CH1: PA8<13>, CH2: PA9<13>, + HRTIM_TIMB: CH1: PA10<13>, CH2: PA11<13>, + HRTIM_TIMC: CH1: PB12<13>, CH2: PB13<13>, + HRTIM_TIMD: CH1: PB14<13>, CH2: PB15<13>, + HRTIM_TIME: CH1: PC8<3>, CH2: PC9<3>, + HRTIM_TIMF: CH1: PC6<13>, CH2: PC7<13> +} diff --git a/src/lib.rs b/src/lib.rs index 470aa878..1b3f118e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,8 @@ pub mod syscfg; pub mod time; pub mod timer; // pub mod watchdog; +#[cfg(feature = "hrtim")] +pub mod hrtim; pub mod independent_watchdog; #[cfg(feature = "usb")] pub mod usb;