diff --git a/Cargo.toml b/Cargo.toml index 329698d1..f8eaffb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,11 +80,12 @@ stm32g431 = ["stm32g4/stm32g431"] stm32g441 = ["stm32g4/stm32g441"] stm32g471 = ["stm32g4/stm32g471"] stm32g473 = ["stm32g4/stm32g473"] -stm32g474 = ["stm32g4/stm32g474"] +stm32g474 = ["stm32g4/stm32g474", "hrtim"] stm32g483 = ["stm32g4/stm32g483"] -stm32g484 = ["stm32g4/stm32g484"] +stm32g484 = ["stm32g4/stm32g484", "hrtim"] stm32g491 = ["stm32g4/stm32g491"] stm32g4a1 = ["stm32g4/stm32g4a1"] +hrtim = [] log-itm = ["cortex-m-log/itm"] log-rtt = [] log-semihost = ["cortex-m-log/semihosting"] @@ -105,3 +106,48 @@ lto = true [[example]] name = "flash_with_rtic" required-features = ["stm32g474"] + +[[example]] +name = "hrtim-adc-trigger" +required-features = ["hrtim"] +path = "examples/hrtim/adc-trigger.rs" + +[[example]] +name = "hrtim-capture" +required-features = ["hrtim"] +path = "examples/hrtim/capture.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" + +[[example]] +name = "hrtim-simple" +required-features = ["hrtim"] +path = "examples/hrtim/simple.rs" diff --git a/examples/adc-one-shot.rs b/examples/adc-one-shot.rs index 7e727430..d67e338d 100644 --- a/examples/adc-one-shot.rs +++ b/examples/adc-one-shot.rs @@ -13,7 +13,7 @@ use stm32g4xx_hal as hal; use cortex_m_rt::entry; -use log::info; +use utils::logger::info; #[macro_use] mod utils; diff --git a/examples/hrtim/adc-trigger.rs b/examples/hrtim/adc-trigger.rs new file mode 100644 index 00000000..7c6facb9 --- /dev/null +++ b/examples/hrtim/adc-trigger.rs @@ -0,0 +1,161 @@ +#![no_std] +#![no_main] + +/// Example showcasing the use of the HRTIM peripheral to trigger the ADC at various points of the switch cycle off HRTIM_TIMA + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::adc; + use stm32g4xx_hal as hal; + + use hal::{ + adc::{ + config::{Continuous, Dma as AdcDma, SampleTime, Sequence}, + AdcClaim, ClockSource, Temperature, Vref, + }, + delay::SYSTDelayExt, + dma::{self, config::DmaConfig, stream::DMAExt, TransferExt}, + gpio::{gpioa::PA8, gpioa::PA9, Alternate, GpioExt, AF13}, + hrtim::compare_register::HrCompareRegister, + hrtim::control::HrControltExt, + hrtim::output::HrOutput, + hrtim::timer::HrTimer, + hrtim::HrPwmAdvExt, + hrtim::Pscl4, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, + }; + + const VREF: f32 = 3.3; + + info!("start"); + + let dp = Peripherals::take().unwrap(); + let cp = cortex_m::Peripherals::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::stream::StreamsTuple(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: PA8> = gpioa.pa8.into_alternate(); + let pin_b: PA9> = gpioa.pa9.into_alternate(); + + // ...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 (mut timer, (mut cr1, _cr2, mut cr3, mut cr4), (mut out1, mut out2), ..) = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &mut rcc) + .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 adc = dp + .ADC1 + .claim(ClockSource::SystemClock, &rcc, &mut delay, true); + + adc.set_external_trigger(( + adc::config::TriggerMode::RisingEdge, + &hr_control.adc_trigger1, + )); + adc.enable_temperature(&dp.ADC12_COMMON); + adc.set_continuous(Continuous::Discontinuous); + adc.reset_sequence(); + adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_640_5); + adc.configure_channel(&Temperature, Sequence::Two, 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(AdcDma::Continuous), + &mut first_buffer[..], + config, + ); + + transfer.start(|adc| adc.start_conversion()); + + out1.enable(); + out2.enable(); + + timer.start(&mut hr_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..3e513ebf --- /dev/null +++ b/examples/hrtim/capture-dma.rs @@ -0,0 +1,134 @@ +#![no_std] +#![no_main] + +/// 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 + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use stm32g4xx_hal as hal; + + use hal::{ + dma::{config::DmaConfig, stream::DMAExt, TransferExt}, + gpio::{gpioa::PA8, Alternate, GpioExt, AF13}, + hrtim::{ + capture, compare_register::HrCompareRegister, control::HrControltExt, external_event, + external_event::ToExternalEventSource, output::HrOutput, timer::HrSlaveTimerCpt, + timer::HrTimer, HrPwmAdvExt, Pscl128, + }, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, + }; + use info; + + 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: PA8> = gpioa.pa8.into_alternate(); + + // 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 (timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, dma_ch) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .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 (mut timer, mut capture, _capture_ch2) = timer.split_capture(); + timer.start(&mut hr_control); + out1.enable(); + + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + info!("Setup DMA"); + let streams = 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 = streams.0.into_circ_peripheral_to_memory_transfer( + capture.enable_dma(dma_ch), + &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(capture::dma_value_to_signed); + 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..c62e84c5 --- /dev/null +++ b/examples/hrtim/capture.rs @@ -0,0 +1,122 @@ +#![no_std] +#![no_main] + +/// 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 + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use stm32g4xx_hal as hal; + + use hal::{ + gpio::{gpioa::PA8, Alternate, GpioExt, AF13}, + hrtim::{ + capture::HrCapture, compare_register::HrCompareRegister, control::HrControltExt, + external_event, external_event::ToExternalEventSource, output::HrOutput, + timer::HrSlaveTimerCpt, timer::HrTimer, HrPwmAdvExt, Pscl128, + }, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, + }; + use info; + + 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: PA8> = gpioa.pa8.into_alternate(); + + // 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 (mut timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, ..) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .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); + timer.start(&mut hr_control); + out1.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 !capture.is_pending() { + continue; + } + let value = capture.get_signed(); + cr1.set_duty(duty as u16); + capture.clear_interrupt(); + info!( + "Capture: {:?}, duty: {}, diff: {}", + value, + old_duty, + value - old_duty as i32 + ); + old_duty = duty; + } + } +} diff --git a/examples/hrtim/eev-comp.rs b/examples/hrtim/eev-comp.rs new file mode 100644 index 00000000..c4a7ee06 --- /dev/null +++ b/examples/hrtim/eev-comp.rs @@ -0,0 +1,142 @@ +#![no_std] +#![no_main] + +/// 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. + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::comparator; + use hal::comparator::{ComparatorExt, ComparatorSplit, Hysteresis}; + use hal::dac::{self, DacExt, DacOut}; + use hal::gpio::gpioa::PA8; + use hal::gpio::Alternate; + use hal::gpio::SignalEdge; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::external_event::{self, ToExternalEventSource}; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::timer_eev_cfg::{EevCfg, EevCfgs}; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::Pscl4; + use hal::hrtim::{control::HrControltExt, output::HrOutput}; + use hal::prelude::*; + use hal::pwm; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::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: PA8> = gpioa.pa8.into_alternate(); + + let dac1ch1 = dp.DAC1.constrain(dac::Dac1IntSig1, &mut rcc); + let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(); + + // 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(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( + pwm::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 (mut timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, ..) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default().eev4(EevCfg::default())) + .period(0xFFFF) + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_rst_event(&eev_input4); + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out1.enable(); + timer.start(&mut hr_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..636564bc --- /dev/null +++ b/examples/hrtim/eev.rs @@ -0,0 +1,110 @@ +#![no_std] +#![no_main] + +/// 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. + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::gpio::gpioa::PA8; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::external_event; + use hal::hrtim::external_event::ToExternalEventSource; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::timer_eev_cfg::EevCfgs; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::Pscl4; + use hal::hrtim::{control::HrControltExt, output::HrOutput}; + use hal::prelude::*; + use hal::pwm; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + + let dp = stm32::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( + pwm::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: PA8> = gpioa.pa8.into_alternate(); + + // . . * . + // . 33% . * . . . + // .-----. .--* .-----. .-----. .----- + //out1 | | | | | | | | | + // | | | * | | | | | + // ------ ----------- -------------- ----------- ----------- + // . . * . . . + // . . * . . . + // . . *--------* . . . + //eev . . | | . . . + // . . | | . . . + // ------------------------- ------------------------------------------ + // . . * . . . + // . . * . . . + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, ..) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default()) + .period(0xFFFF) + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_rst_event(&eev_input3); + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out1.enable(); + timer.start(&mut hr_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..9131f42f --- /dev/null +++ b/examples/hrtim/flt-comp.rs @@ -0,0 +1,155 @@ +#![no_std] +#![no_main] + +/// 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. + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::comparator::{ComparatorExt, ComparatorSplit, Config, Hysteresis}; + use hal::dac::{Dac3IntSig1, DacExt, DacOut}; + use hal::gpio::gpioa::PA8; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::fault::FaultAction; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::Pscl4; + use hal::hrtim::{control::HrControltExt, output::HrOutput}; + use hal::prelude::*; + use hal::pwm::FaultMonitor; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + use stm32g4xx_hal::adc::AdcClaim; + use stm32g4xx_hal::pwr::PwrExt; + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::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 mut adc1 = dp.ADC1.claim_and_configure( + hal::adc::ClockSource::SystemClock, + &rcc, + hal::adc::config::AdcConfig::default() + .clock_mode(hal::adc::config::ClockMode::Synchronous_Div_4), + &mut delay, + false, + ); + + 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(); + + // 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 = gpioc.pc1.into_analog(); + let comp3 = comp3 + .comparator( + &pc1, + &dac, + Config::default() + .hysteresis(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_comp(&comp3) + .polarity(hal::pwm::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: PA8> = gpioa.pa8.into_alternate(); + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, ..) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .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); + //unsafe {((HRTIM_COMMON::ptr() as *mut u8).offset(0x14) as *mut u32).write_volatile(1); } + out1.enable(); + timer.start(&mut hr_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(), + 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..a1a6879e --- /dev/null +++ b/examples/hrtim/flt.rs @@ -0,0 +1,123 @@ +#![no_std] +#![no_main] + +/// 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. + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::gpio::gpioa::PA8; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::fault::FaultAction; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::Pscl4; + use hal::hrtim::{control::HrControltExt, output::HrOutput}; + use hal::prelude::*; + use hal::pwm::FaultMonitor; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use hal::time::ExtU32; + use stm32g4xx_hal as hal; + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::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 fault_source3 = flt_inputs + .fault_input3 + .bind_pin(gpiob.pb10.into_pull_down_input()) + .polarity(hal::pwm::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: PA8> = gpioa.pa8.into_alternate(); + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), mut out1, ..) = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .period(0xFFFF) + //.with_fault_source(fault_source1) + //.with_fault_source(fault_source2) + .with_fault_source(fault_source3) // Set fault source + //.with_fault_source(fault_source4) + //.with_fault_source(fault_source5) + //.with_fault_source(fault_source6) + .fault_action1(FaultAction::ForceInactive) + .fault_action2(FaultAction::ForceInactive) + // 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 + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + //unsafe {((HRTIM_COMMON::ptr() as *mut u8).offset(0x14) as *mut u32).write_volatile(1); } + out1.enable(); + timer.start(&mut hr_control); + + info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + info!("State: {}", out1.get_state()); + } + if hr_control.fault_3.is_fault_active() { + hr_control.fault_3.clear_fault(); // Clear fault every 5s + out1.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..b29ceffe --- /dev/null +++ b/examples/hrtim/hrtim.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] + +/// Add description + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use fugit::ExtU32; + use hal::gpio::gpioa::PA8; + use hal::gpio::gpioa::PA9; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::control::HrControltExt; + use hal::hrtim::output::HrOutput; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::Pscl4; + use hal::prelude::*; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + extern crate cortex_m_rt as rt; + + info!("Initializing..."); + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::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: PA8> = gpioa.pa8.into_alternate(); + let pin_b: PA9> = gpioa.pa9.into_alternate(); + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), (mut out1, mut out2), ..) = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &mut rcc) + .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(); + + loop { + // Step frequency from 18kHz to about 180kHz(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..a74dafff --- /dev/null +++ b/examples/hrtim/master.rs @@ -0,0 +1,141 @@ +#![no_std] +#![no_main] + +/// Add description + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use fugit::ExtU32; + use hal::gpio::gpioa::PA8; + use hal::gpio::gpioa::PA9; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::control::HrControltExt; + use hal::hrtim::output::HrOutput; + use hal::hrtim::timer::HrSlaveTimer; + use hal::hrtim::timer::HrTimer; + use hal::hrtim::HrPwmAdvExt; + use hal::hrtim::{HrTimerMode, MasterPreloadSource, PreloadSource, Pscl4}; + use hal::prelude::*; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + extern crate cortex_m_rt as rt; + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::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: PA8> = gpioa.pa8.into_alternate(); + let pin_b: PA9> = gpioa.pa9.into_alternate(); + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), (mut out1, mut out2), ..) = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &mut rcc) + .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 (mut mtimer, (mut mcr1, _mcr2, _mcr3, _mcr4), ..) = dp + .HRTIM_MASTER + .pwm_advanced((), &mut rcc) + .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(); + + let tima = unsafe { &*stm32g4xx_hal::stm32::HRTIM_TIMA::ptr() }; + info!("set1r: {}", tima.seta1r.read().bits()); + info!("rst1r: {}", tima.rsta1r.read().bits()); + + info!("set2r: {}", tima.seta2r.read().bits()); + info!("rst2r: {}", tima.rsta2r.read().bits()); + + info!("Running"); + + loop { + // Step frequency from 18kHz to about 180kHz(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/examples/hrtim/simple.rs b/examples/hrtim/simple.rs new file mode 100644 index 00000000..e493adac --- /dev/null +++ b/examples/hrtim/simple.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] + +/// Example showcasing the use of the HRTIM peripheral generating two 20kHz pwm signals. One with 33% duty and the other with 50% + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::gpio::gpioa::PA8; + use hal::gpio::gpioa::PA9; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::hrtim::{control::HrControltExt, HrPwmExt}; + use hal::prelude::*; + use hal::pwr::PwrExt; + use hal::rcc; + use hal::stm32; + use hal::time::RateExtU32; + use stm32g4xx_hal as hal; + extern crate cortex_m_rt as rt; + + info!("Initializing..."); + + let dp = stm32::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 pin_a: PA8> = gpioa.pa8.into_alternate(); + let pin_b: PA9> = gpioa.pa9.into_alternate(); + + // . . . + // . 33% . . + // ---- .---- .---- + //out1 | | | | | | + // | | | | | | + // ------ ------------ ------------ --------- + // . . + // 50% . . + // -------- .-------- .-------- + //out2 | | | | | | + // | | | | | | + // ------ -------- -------- ----- + // . . . + // . . . + + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + let (mut p1, mut p2) = + dp.HRTIM_TIMA + .pwm((pin_a, pin_b), 20_u32.kHz(), &mut hr_control, &mut rcc); + let max_duty = p1.get_max_duty(); + + p1.set_duty(max_duty / 3); // Set output 1 to about 33% + p2.set_duty(max_duty / 2); // Set output 2 to 50% + + // Enable the outputs + p1.enable(); + p2.enable(); + + info!("Running"); + + loop { + cortex_m::asm::nop() + } +} diff --git a/src/adc.rs b/src/adc.rs index e131aceb..14f90318 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1793,6 +1793,11 @@ macro_rules! adc { /// Calibrate the adc for #[inline(always)] pub fn calibrate(&mut self, it: config::InputType) { + let cr = self.adc_reg.cr.read(); + assert!(cr.aden().bit_is_clear()); + assert!(cr.adstart().bit_is_clear()); + assert!(cr.jadstart().bit_is_clear()); + match it { config::InputType::SingleEnded => { self.adc_reg.cr.modify(|_, w| w.adcaldif().clear_bit() ); @@ -1919,6 +1924,7 @@ macro_rules! adc { /// Resets the end-of-conversion flag #[inline(always)] pub fn clear_end_of_conversion_flag(&mut self) { + //defmt::error!("Clear!"); self.adc_reg.isr.modify(|_, w| w.eoc().set_bit()); } @@ -2233,8 +2239,8 @@ macro_rules! adc { /// Sets which external trigger to use and if it is disabled, rising, falling or both #[inline(always)] - pub fn set_external_trigger(&mut self, (edge, extsel): (config::TriggerMode, $trigger_type)) { - self.adc.set_external_trigger( (edge, extsel) ) + pub fn set_external_trigger>(&mut self, (edge, extsel): (config::TriggerMode, T)) { + self.adc.set_external_trigger( (edge, extsel.into()) ) } /// Sets auto delay to true or false diff --git a/src/dma/stream.rs b/src/dma/stream.rs index d166b48e..459d69c3 100644 --- a/src/dma/stream.rs +++ b/src/dma/stream.rs @@ -182,7 +182,7 @@ impl StreamsTuple { // the stream macro_rules! dma_stream { ($(($name:ident, $number:expr, - regs => $ccr:ident, $cparX:ident, $cmarX:ident, $cndtrX:ident, + regs => $chX:ident, fields => $tcif:ident, $htif:ident, $teif:ident, $gif:ident, $tcisr:ident, $htisr:ident, $teisr:ident, $gisr:ident, dmamux => $dma1_cXcr:ident, $dma2_cXcr:ident, ) ),+$(,)*) => { @@ -266,28 +266,28 @@ macro_rules! dma_stream { #[inline(always)] unsafe fn enable(&mut self) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = &*I::ptr(); - dma.$ccr.modify(|_, w| w.en().set_bit()); + let dma_ch = &unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.en().set_bit()); } #[inline(always)] fn is_enabled() -> bool { //NOTE(unsafe) Atomic read with no side effects - let dma = unsafe { &*I::ptr() }; - dma.$ccr.read().en().bit_is_set() + let dma_ch = &unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.read().en().bit_is_set() } fn disable(&mut self) { if Self::is_enabled() { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; + let dma_ch = &unsafe { &*I::ptr() }.$chX(); // Aborting an on-going transfer might cause interrupts to fire, disable // them let interrupts = Self::get_interrupts_enable(); self.disable_interrupts(); - dma.$ccr.modify(|_, w| w.en().clear_bit()); + dma_ch.cr.modify(|_, w| w.en().clear_bit()); while Self::is_enabled() {} self.clear_interrupts(); @@ -313,14 +313,14 @@ macro_rules! dma_stream { #[inline(always)] fn set_priority(&mut self, priority: config::Priority) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| unsafe { w.pl().bits(priority.bits()) }); + let dma_ch = &unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| unsafe { w.pl().bits(priority.bits()) }); } #[inline(always)] fn disable_interrupts(&mut self) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dmacr = &unsafe { &*I::ptr() }.$ccr; + let dmacr = &unsafe { &*I::ptr() }.$chX().cr; dmacr.modify(|_, w| w .tcie().clear_bit() .teie().clear_bit() @@ -332,8 +332,8 @@ macro_rules! dma_stream { #[inline(always)] fn enable_interrupts(&mut self, interrupt: Self::Interrupts) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| w + let dma_ch = &unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w .tcie().bit(interrupt.transfer_complete) .teie().bit(interrupt.transfer_error) .htie().bit(interrupt.half_transfer) @@ -343,8 +343,8 @@ macro_rules! dma_stream { #[inline(always)] fn get_interrupts_enable() -> Self::Interrupts { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - let cr = dma.$ccr.read(); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + let cr = dma_ch.cr.read(); DmaInterrupts { transfer_complete: cr.tcie().bit_is_set(), @@ -357,7 +357,7 @@ macro_rules! dma_stream { #[inline(always)] fn set_transfer_complete_interrupt_enable(&mut self, transfer_complete_interrupt: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dmacr = &unsafe { &*I::ptr() }.$ccr; + let dmacr = &unsafe { &*I::ptr() }.$chX().cr; dmacr.modify(|_, w| w.tcie().bit(transfer_complete_interrupt)); let _ = dmacr.read(); let _ = dmacr.read(); // Delay 2 peripheral clocks @@ -366,7 +366,7 @@ macro_rules! dma_stream { #[inline(always)] fn set_transfer_error_interrupt_enable(&mut self, transfer_error_interrupt: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dmacr = &unsafe { &*I::ptr() }.$ccr; + let dmacr = &unsafe { &*I::ptr() }.$chX().cr; dmacr.modify(|_, w| w.teie().bit(transfer_error_interrupt)); let _ = dmacr.read(); let _ = dmacr.read(); // Delay 2 peripheral clocks @@ -375,7 +375,7 @@ macro_rules! dma_stream { #[inline(always)] fn set_half_transfer_interrupt_enable(&mut self, half_transfer_interrupt: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dmacr = &unsafe { &*I::ptr() }.$ccr; + let dmacr = &unsafe { &*I::ptr() }.$chX().cr; dmacr.modify(|_, w| w.htie().bit(half_transfer_interrupt)); let _ = dmacr.read(); let _ = dmacr.read(); // Delay 2 peripheral clocks @@ -401,70 +401,70 @@ macro_rules! dma_stream { #[inline(always)] unsafe fn set_peripheral_address(&mut self, value: u32) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = &*I::ptr(); - dma.$cparX.write(|w| w.pa().bits(value)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.par.write(|w| w.pa().bits(value)); } #[inline(always)] unsafe fn set_memory_address(&mut self, value: u32) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = &*I::ptr(); - dma.$cmarX.write(|w| w.ma().bits(value) ); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.mar.write(|w| w.ma().bits(value) ); } #[inline(always)] fn get_memory_address(&self) -> u32 { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$cmarX.read().ma().bits() + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.mar.read().ma().bits() } #[inline(always)] fn set_number_of_transfers(&mut self, value: u16) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$cndtrX.write(|w| unsafe {w.ndt().bits(value) }); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.ndtr.write(|w| unsafe {w.ndt().bits(value) }); } #[inline(always)] fn get_number_of_transfers() -> u16 { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$cndtrX.read().ndt().bits() + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.ndtr.read().ndt().bits() } #[inline(always)] unsafe fn set_memory_size(&mut self, size: u8) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = &*I::ptr(); - dma.$ccr.modify(|_, w| w.msize().bits(size)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.msize().bits(size)); } #[inline(always)] unsafe fn set_peripheral_size(&mut self, size: u8) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = &*I::ptr(); - dma.$ccr.modify(|_, w| w.psize().bits(size)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.psize().bits(size)); } #[inline(always)] fn set_memory_increment(&mut self, increment: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| w.minc().bit(increment)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.minc().bit(increment)); } #[inline(always)] fn set_peripheral_increment(&mut self, increment: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| w.pinc().bit(increment)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.pinc().bit(increment)); } #[inline(always)] fn set_direction(&mut self, direction: DmaDirection) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| { + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| { match direction { DmaDirection::PeripheralToMemory => w.dir().clear_bit().mem2mem().clear_bit(), @@ -479,8 +479,8 @@ macro_rules! dma_stream { #[inline(always)] fn set_circular_buffer(&mut self, circular_buffer: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dma = unsafe { &*I::ptr() }; - dma.$ccr.modify(|_, w| w.circ().bit(circular_buffer)); + let dma_ch = unsafe { &*I::ptr() }.$chX(); + dma_ch.cr.modify(|_, w| w.circ().bit(circular_buffer)); } } @@ -505,7 +505,7 @@ macro_rules! dma_stream { #[inline(always)] pub fn set_half_transfer_interrupt_enable(&mut self, half_transfer_interrupt: bool) { //NOTE(unsafe) We only access the registers that belongs to the StreamX - let dmacr = &unsafe { &*I::ptr() }.$ccr; + let dmacr = &unsafe { &*I::ptr() }.$chX().cr; dmacr.modify(|_, w| w.htie().bit(half_transfer_interrupt)); let _ = dmacr.read(); let _ = dmacr.read(); // Delay 2 peripheral clocks @@ -530,49 +530,49 @@ dma_stream!( // zero. May need updating if it gets fixed upstream. ( Stream0, 0, - regs => ccr1, cpar1, cmar1, cndtr1, + regs => ch1, fields => tcif1, htif1, teif1, gif1, tcif1, htif1, teif1, gif1, dmamux => c0cr, c8cr, ), ( Stream1, 1, - regs => ccr2, cpar2, cmar2, cndtr2, + regs => ch2, fields => tcif2, htif2, teif2, gif2, tcif2, htif2, teif2, gif2, dmamux => c1cr, c9cr, ), ( Stream2, 2, - regs => ccr3, cpar3, cmar3, cndtr3, + regs => ch3, fields => tcif3, htif3, teif3, gif3, tcif3, htif3, teif3, gif3, dmamux => c2cr, c10cr, ), ( Stream3, 3, - regs => ccr4, cpar4, cmar4, cndtr4, + regs => ch4, fields => tcif4, htif4, teif4, gif4, tcif4, htif4, teif4, gif4, dmamux => c3cr, c11cr, ), ( Stream4, 4, - regs => ccr5, cpar5, cmar5, cndtr5, + regs => ch5, fields => tcif5, htif5, teif5, gif5, tcif5, htif5, teif5, gif5, dmamux => c4cr, c12cr, ), ( Stream5, 5, - regs => ccr6, cpar6, cmar6, cndtr6, + regs => ch6, fields => tcif6, htif6, teif6, gif6, tcif6, htif6, teif6, gif6, dmamux => c5cr, c13cr, ), ( Stream6, 6, - regs => ccr7, cpar7, cmar7, cndtr7, + regs => ch7, fields => tcif7, htif7, teif7, gif7, tcif7, htif7, teif7, gif7, dmamux => c6cr, c14cr, ), ( Stream7, 7, - regs => ccr8, cpar8, cmar8, cndtr8, + regs => ch8, fields => tcif8, htif8, teif8, gif8, tcif8, htif8, teif8, gif8, dmamux => c7cr, c15cr, ), diff --git a/src/dma/transfer.rs b/src/dma/transfer.rs index bf287076..b974ac00 100644 --- a/src/dma/transfer.rs +++ b/src/dma/transfer.rs @@ -383,14 +383,38 @@ where read } + /// Read all immediately available values without blocking pub fn read_available<'a>( &mut self, - data: &'a mut [>::MemSize], + dst: &'a mut [>::MemSize], + ) -> &'a mut [>::MemSize] { + self.read_available_multiple(1, dst) + } + + /// Same as [`CircTransfer::read_available`] but only read a whole multiple number of elements + /// + /// Example: + /// ``` + /// ... + /// // There are 7 values in the buffer + /// assert_eq!(transfer.read_available_multiple(3, &mut dst).len(), 6); + /// + /// // There are 3 values in the buffer + /// assert_eq!(transfer.read_available_multiple(2, &mut dst).len(), 2); + /// + /// // There are 1 values in the buffer + /// assert_eq!(transfer.read_available_multiple(2, &mut dst).len(), 0); + /// ``` + pub fn read_available_multiple<'a>( + &mut self, + multiple: u8, + dst: &'a mut [>::MemSize], ) -> &'a mut [>::MemSize] { let blen = unsafe { self.transfer.buf.static_write_buffer().1 }; let available = self.elements_available(); - let len = data.len().min(available).min(blen - 1); - let result = &mut data[0..len]; + let len = dst.len().min(available).min(blen - 1); + let len = len - (len % usize::from(multiple)); + let result = &mut dst[0..len]; self.read_exact(result); result diff --git a/src/hrtim/adc_trigger.rs b/src/hrtim/adc_trigger.rs new file mode 100644 index 00000000..2782c10e --- /dev/null +++ b/src/hrtim/adc_trigger.rs @@ -0,0 +1,20 @@ +use core::marker::PhantomData; + +pub trait Adc13Trigger { + const BITS: u32; +} + +pub trait Adc24Trigger { + const BITS: u32; +} + +pub trait Adc579Trigger { + const BITS: u32; +} + +pub trait Adc6810Trigger { + const BITS: u32; +} + +pub struct TimerReset(pub(crate) PhantomData); +pub struct TimerPeriod(pub(crate) PhantomData); diff --git a/src/hrtim/capture.rs b/src/hrtim/capture.rs new file mode 100644 index 00000000..35b505c4 --- /dev/null +++ b/src/hrtim/capture.rs @@ -0,0 +1,198 @@ +use super::timer; +use crate::dma::mux::DmaMuxResources; +use crate::dma::traits::TargetAddress; +use crate::dma::PeripheralToMemory; +use crate::stm32::{HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF}; +use core::marker::PhantomData; + +pub struct Ch1; +pub struct Ch2; + +pub struct Dma; +pub struct NoDma; + +pub struct HrCapt { + _x: PhantomData<(TIM, PSCL, CH, DMA)>, +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug)] +pub enum CountingDirection { + Up = 0, + Down = 1, +} + +/// Implemented for +/// * TIM's update event +/// * EEVT1-10 +/// TODO: This sould be implemeted +/// * All neighbor timers CMP1, CPM2, OUT1_RST and OUT1_SET events +pub trait CaptureEvent { + const BITS: u32; +} + +pub trait HrCapture { + fn get(&self) -> (u16, CountingDirection); + + /// Get number of ticks relative to beginning of upcounting + /// + /// where captures during down counting count as negative (before the upcount) + /// + /// ```` + /// Counter + /// ---------------------------------- <--- period + /// \ ^ / + /// \ | / + /// \ | / + /// \ | / + /// Down count \ | / Up count + /// \|/ + /// <-------------- 0 --------------> t + /// Negative result | positive result + /// ```` + fn get_signed(&self) -> i32 { + let (value, dir) = self.get(); + + match dir { + CountingDirection::Up => i32::from(value), + CountingDirection::Down => -i32::from(value), + } + } + + fn clear_interrupt(&mut self); + + fn is_pending(&self) -> bool; +} + +pub fn dma_value_to_dir_and_value(x: u32) -> (u16, CountingDirection) { + let value = (x & 0xFFFF) as u16; + match x & 1 << 16 != 0 { + true => (value, CountingDirection::Down), + false => (value, CountingDirection::Up), + } +} + +pub fn dma_value_to_signed(x: u32) -> i32 { + let (value, dir) = dma_value_to_dir_and_value(x); + + match dir { + CountingDirection::Up => i32::from(value), + CountingDirection::Down => -i32::from(value), + } +} + +macro_rules! impl_capture { + ($($TIMX:ident: $CH:ident, $cptXYr:ident, $cptXYcr:ident, $cptXx:ident, $dier:ident, $icr:ident, $isr:ident, $cptXie:ident, $cptXde:ident, $cptXc:ident, $cptX:ident, $mux:expr),+) => {$( + impl HrCapt<$TIMX, PSCL, $CH, NoDma> { + /// Add event to capture + /// + /// If multiple events are added, they will be ORed together meaning + /// that a capture will be trigger if any one of the events triggers + pub fn add_event>(&mut self, _event: &E) { + let tim = unsafe { &*$TIMX::ptr() }; + + // SAFETY: We are the only one with access to cptXYcr + unsafe { + tim.$cptXYcr.modify(|r, w| w.bits(r.bits() | E::BITS)); + } + } + + /// Remove event to capture + pub fn remove_event>(&mut self, _event: &E) { + let tim = unsafe { &*$TIMX::ptr() }; + + // SAFETY: We are the only one with access to cptXYcr + unsafe { + tim.$cptXYcr.modify(|r, w| w.bits(r.bits() & !E::BITS)); + } + } + + /// Force capture trigger now + pub fn trigger_now(&mut self) { + // SAFETY: We are the only one with access to cptXYcr + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cptXYcr.modify(|_, w| w.swcpt().set_bit()); + } + + // TODO: It would be sufficient to instead of hr_control only require exclusive access to the owning timer + // however that would be hard to do since typically the capture device is a field of that same timer. + // Would it make more sense to have this method direcly on HrTim instead? + pub fn enable_interrupt(&mut self, enable: bool, _hr_control: &mut super::HrPwmControl) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$dier.modify(|_r, w| w.$cptXie().bit(enable)); + } + + pub fn enable_dma(self, _ch: timer::DmaChannel<$TIMX>) -> HrCapt<$TIMX, PSCL, $CH, Dma> { + // SAFETY: We own the only insance of this timers dma channel, no one else can do this + let tim = unsafe { &*$TIMX::ptr() }; + tim.$dier.modify(|_r, w| w.$cptXde().set_bit()); + HrCapt { + _x: PhantomData + } + } + } + + impl HrCapture for HrCapt<$TIMX, PSCL, $CH, DMA> { + fn get(&self) -> (u16, CountingDirection) { + let tim = unsafe { &*$TIMX::ptr() }; + let data = tim.$cptXYr.read(); + + let dir = match data.dir().bit() { + true => CountingDirection::Down, + false => CountingDirection::Up, + }; + let value = data.$cptXx().bits(); + + (value, dir) + } + + fn clear_interrupt(&mut self) { + let tim = unsafe { &*$TIMX::ptr() }; + + // No need for exclusive access since this is a write only register + tim.$icr.write(|w| w.$cptXc().set_bit()); + } + + fn is_pending(&self) -> bool { + let tim = unsafe { &*$TIMX::ptr() }; + + // No need for exclusive access since this is a read only register + tim.$isr.read().$cptX().bit() + } + } + + unsafe impl TargetAddress for HrCapt<$TIMX, PSCL, $CH, Dma> { + #[inline(always)] + fn address(&self) -> u32 { + let tim = unsafe { &*$TIMX::ptr() }; + &tim.$cptXYr as *const _ as u32 + } + + type MemSize = u32; + + const REQUEST_LINE: Option = Some($mux as u8); + } + )+}; +} + +impl_capture! { + HRTIM_TIMA: Ch1, cpt1ar, cpt1acr, cpt1x, timadier, timaicr, timaisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIMA, + HRTIM_TIMA: Ch2, cpt2ar, cpt2acr, cpt2x, timadier, timaicr, timaisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIMA, + + HRTIM_TIMB: Ch1, cpt1br, cpt1bcr, cpt1x, timbdier, timbicr, timbisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIMB, + HRTIM_TIMB: Ch2, cpt2br, cpt2bcr, cpt2x, timbdier, timbicr, timbisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIMB, + + HRTIM_TIMC: Ch1, cpt1cr, cpt1ccr, cpt1x, timcdier, timcicr, timcisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIMC, + HRTIM_TIMC: Ch2, cpt2cr, cpt2ccr, cpt2x, timcdier, timcicr, timcisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIMC, + + HRTIM_TIMD: Ch1, cpt1dr, cpt1dcr, cpt1x, timddier, timdicr, timdisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIMD, + HRTIM_TIMD: Ch2, cpt2dr, cpt2dcr, cpt2x, timddier, timdicr, timdisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIMD, + + HRTIM_TIME: Ch1, cpt1er, cpt1ecr, cpt1x, timedier, timeicr, timeisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIME, + HRTIM_TIME: Ch2, cpt2er, cpt2ecr, cpt2x, timedier, timeicr, timeisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIME, + + HRTIM_TIMF: Ch1, cpt1fr, cpt1fcr, cpt1x, timfdier, timficr, timfisr, cpt1ie, cpt1de, cpt1c, cpt1, DmaMuxResources::HRTIM_TIMF, + HRTIM_TIMF: Ch2, cpt2fr, cpt2fcr, cpt2x, timfdier, timficr, timfisr, cpt2ie, cpt2de, cpt2c, cpt2, DmaMuxResources::HRTIM_TIMF +} diff --git a/src/hrtim/compare_register.rs b/src/hrtim/compare_register.rs new file mode 100644 index 00000000..8beb4103 --- /dev/null +++ b/src/hrtim/compare_register.rs @@ -0,0 +1,186 @@ +use core::marker::PhantomData; + +use crate::stm32::{ + HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF, +}; + +pub trait HrCompareRegister { + fn get_duty(&self) -> u16; + fn set_duty(&mut self, duty: u16); +} + +pub struct HrCr1(PhantomData<(TIM, PSCL)>); +pub struct HrCr2(PhantomData<(TIM, PSCL)>); +pub struct HrCr3(PhantomData<(TIM, PSCL)>); +pub struct HrCr4(PhantomData<(TIM, PSCL)>); + +use super::adc_trigger::Adc13Trigger as Adc13; +use super::adc_trigger::Adc24Trigger as Adc24; +use super::adc_trigger::Adc579Trigger as Adc579; +use super::adc_trigger::Adc6810Trigger as Adc6810; + +macro_rules! hrtim_cr_helper { + (HRTIM_MASTER: $cr_type:ident: + $cmpXYr:ident, $cmpYx:ident, + [$(($Trigger:ty: $trigger_bits:expr)),*], + [$(($event_dst:ident, $tim_event_index:expr)),*], + $bit_index:literal + ) => { + // Strip bit_index since master timer has other bits that are common across all destinations + hrtim_cr_helper!(HRTIM_MASTER: $cr_type: $cmpXYr, $cmpYx, [$(($Trigger: $trigger_bits)),*], [$(($event_dst, $tim_event_index)),*]); + }; + + ($TIMX:ident: $cr_type:ident: + $cmpXYr:ident, $cmpYx:ident, + [$(($Trigger:ty: $trigger_bits:expr)),*], + [$(($event_dst:ident, $tim_event_index:expr)),*] + $(, $bit_index:literal)* + ) => { + impl HrCompareRegister for $cr_type<$TIMX, PSCL> { + fn get_duty(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.read().$cmpYx().bits() + } + fn set_duty(&mut self, duty: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.write(|w| unsafe { w.$cmpYx().bits(duty) }); + } + } + + $( + /// Compare match event + impl super::event::EventSource<$TIMX, PSCL> for $cr_type<$TIMX, PSCL> { + const BITS: u32 = 1 << $bit_index; + } + )* + + $( + /// Compare match event for neighbor timer + impl super::event::EventSource<$event_dst, PSCL> for $cr_type<$TIMX, PSCL> { + const BITS: u32 = 1 << ($tim_event_index + 11); // TIMEVNT1 is at bit 12, TIMEVNT2 at bit 13 etc + } + )* + + $( + impl $Trigger for $cr_type<$TIMX, PSCL> { + const BITS: u32 = $trigger_bits; + } + )* + }; +} + +macro_rules! hrtim_cr { + ($($TIMX:ident: [ + [$cmpX1r:ident, $cmp1x:ident, [$(($cr1_trigger:ident: $cr1_trigger_bits:expr)),*], [$(($cr1_event_dst:ident, $cr1_tim_event_index:expr)),*]], + [$cmpX2r:ident, $cmp2x:ident, [$(($cr2_trigger:ident: $cr2_trigger_bits:expr)),*], [$(($cr2_event_dst:ident, $cr2_tim_event_index:expr)),*]], + [$cmpX3r:ident, $cmp3x:ident, [$(($cr3_trigger:ident: $cr3_trigger_bits:expr)),*], [$(($cr3_event_dst:ident, $cr3_tim_event_index:expr)),*]], + [$cmpX4r:ident, $cmp4x:ident, [$(($cr4_trigger:ident: $cr4_trigger_bits:expr)),*], [$(($cr4_event_dst:ident, $cr4_tim_event_index:expr)),*]] + ]),+) => {$( + hrtim_cr_helper!($TIMX: HrCr1: $cmpX1r, $cmp1x, [$(($cr1_trigger: $cr1_trigger_bits)),*], [$(($cr1_event_dst, $cr1_tim_event_index)),*], 3); + hrtim_cr_helper!($TIMX: HrCr2: $cmpX2r, $cmp2x, [$(($cr2_trigger: $cr2_trigger_bits)),*], [$(($cr2_event_dst, $cr2_tim_event_index)),*], 4); + hrtim_cr_helper!($TIMX: HrCr3: $cmpX3r, $cmp3x, [$(($cr3_trigger: $cr3_trigger_bits)),*], [$(($cr3_event_dst, $cr3_tim_event_index)),*], 5); + hrtim_cr_helper!($TIMX: HrCr4: $cmpX4r, $cmp4x, [$(($cr4_trigger: $cr4_trigger_bits)),*], [$(($cr4_event_dst, $cr4_tim_event_index)),*], 6); + )+}; +} + +// See RM0440 Table 218. 'Events mapping across timer A to F' +hrtim_cr! { + HRTIM_MASTER: [ + [mcmp1r, mcmp1, [(Adc13: 1 << 0), (Adc24: 1 << 0), (Adc579: 0), (Adc6810: 0) ], []], + [mcmp2r, mcmp2, [(Adc13: 1 << 1), (Adc24: 1 << 1), (Adc579: 1), (Adc6810: 1) ], []], + [mcmp3r, mcmp3, [(Adc13: 1 << 2), (Adc24: 1 << 2), (Adc579: 2), (Adc6810: 2) ], []], + [mcmp4r, mcmp4, [(Adc13: 1 << 3), (Adc24: 1 << 3), (Adc579: 3), (Adc6810: 3) ], []] + ], + + HRTIM_TIMA: [ + [cmp1ar, cmp1x, [ ], [(HRTIM_TIMB, 1), (HRTIM_TIMD, 1) ]], + [cmp2ar, cmp2x, [ (Adc24: 1 << 10), (Adc6810: 10)], [(HRTIM_TIMB, 2), (HRTIM_TIMC, 1) ]], + [cmp3ar, cmp3x, [(Adc13: 1 << 11), (Adc579: 10) ], [(HRTIM_TIMC, 2), (HRTIM_TIMF, 1) ]], + [cmp4ar, cmp4x, [(Adc13: 1 << 12), (Adc24: 1 << 12), (Adc579: 11), (Adc6810: 11)], [(HRTIM_TIMD, 2), (HRTIM_TIME, 1) ]] + ], + + HRTIM_TIMB: [ + [cmp1br, cmp1x, [ ], [(HRTIM_TIMA, 1), (HRTIM_TIMF, 2) ]], + [cmp2br, cmp2x, [ (Adc24: 1 << 14), (Adc6810: 13)], [(HRTIM_TIMA, 2), (HRTIM_TIMC, 3), (HRTIM_TIMD, 3)]], + [cmp3br, cmp3x, [(Adc13: 1 << 16), (Adc579: 14) ], [(HRTIM_TIMC, 4), (HRTIM_TIME, 2) ]], + [cmp4br, cmp4x, [(Adc13: 1 << 17), (Adc24: 1 << 16), (Adc579: 15), (Adc6810: 14)], [(HRTIM_TIMD, 4), (HRTIM_TIME, 3), (HRTIM_TIMF, 3)]] + ], + + HRTIM_TIMC: [ + [cmp1cr, cmp1x, [ ], [(HRTIM_TIME, 4), (HRTIM_TIMF, 4) ]], + [cmp2cr, cmp2x, [ (Adc24: 1 << 18), (Adc6810: 16)], [(HRTIM_TIMA, 3), (HRTIM_TIME, 5) ]], + [cmp3cr, cmp3x, [(Adc13: 1 << 21), (Adc579: 18) ], [(HRTIM_TIMA, 4), (HRTIM_TIMB, 3) ]], + [cmp4cr, cmp4x, [(Adc13: 1 << 22), (Adc24: 1 << 20), (Adc579: 19), (Adc6810: 17)], [(HRTIM_TIMB, 4), (HRTIM_TIMD, 5), (HRTIM_TIMF, 5)]] + ], + + HRTIM_TIMD: [ + [cmp1dr, cmp1x, [ ], [(HRTIM_TIMA, 5), (HRTIM_TIME, 6) ]], + [cmp2dr, cmp2x, [ (Adc24: 1 << 23), (Adc6810: 20)], [(HRTIM_TIMA, 6), (HRTIM_TIMC, 5), (HRTIM_TIME, 7)]], + [cmp3dr, cmp3x, [(Adc13: 1 << 25), (Adc579: 21) ], [(HRTIM_TIMB, 5), (HRTIM_TIMF, 6) ]], + [cmp4dr, cmp4x, [(Adc13: 1 << 26), (Adc24: 1 << 25), (Adc579: 22), (Adc6810: 21)], [(HRTIM_TIMB, 6), (HRTIM_TIMC, 6), (HRTIM_TIMF, 7)]] + ], + + HRTIM_TIME: [ + [cmp1er, cmp1x, [ ], [(HRTIM_TIMB, 7), (HRTIM_TIMD, 6) ]], + [cmp2er, cmp2x, [ (Adc24: 1 << 28), (Adc6810: 24)], [(HRTIM_TIMB, 8), (HRTIM_TIMF, 8) ]], + [cmp3er, cmp3x, [(Adc13: 1 << 29), (Adc24: 1 << 29), (Adc579: 24), (Adc6810: 25)], [(HRTIM_TIMA, 7), (HRTIM_TIMC, 7), (HRTIM_TIMF, 9)]], + [cmp4er, cmp4x, [(Adc13: 1 << 30), (Adc24: 1 << 30), (Adc579: 25), (Adc6810: 26)], [(HRTIM_TIMA, 8), (HRTIM_TIMC, 8), (HRTIM_TIMD, 7)]] + ], + + HRTIM_TIMF: [ + [cmp1fr, cmp1x, [ (Adc24: 1 << 15) ], [(HRTIM_TIMD, 8) ]], + [cmp2fr, cmp2x, [(Adc13: 1 << 10), (Adc24: 1 << 11), (Adc579: 27), (Adc6810: 28)], [(HRTIM_TIMC, 9) ]], + [cmp3fr, cmp3x, [(Adc13: 1 << 15), (Adc579: 28), (Adc6810: 29)], [(HRTIM_TIMB, 9), (HRTIM_TIMD, 9), (HRTIM_TIME, 8)]], + [cmp4fr, cmp4x, [(Adc13: 1 << 20), (Adc24: 1 << 19), (Adc579: 29), (Adc6810: 30)], [(HRTIM_TIMA, 9), (HRTIM_TIME, 9) ]] + ] +} + +macro_rules! hrtim_master_cr { + ($($cr_type:ident: $cr_index:expr),*) => {$( + /// Compare match event for neighbor timer + impl super::event::EventSource for $cr_type { + const BITS: u32 = 1 << ($cr_index + 7); // MSTCMP1 is at bit 8 etc + } + + impl super::event::TimerResetEventSource for $cr_type { + const BITS: u32 = 1 << ($cr_index + 4); // MSTCMP1 is at bit 5 + } + )*}; +} + +hrtim_master_cr! { + HrCr1: 1, + HrCr2: 2, + HrCr3: 3, + HrCr4: 4 +} + +macro_rules! hrtim_timer_rst { + ($($TIMX:ident: $cr_type:ident: $bit_index:literal),*) => {$( + impl super::event::TimerResetEventSource for $cr_type<$TIMX, PSCL> { + const BITS: u32 = 1 << $bit_index; + } + )*}; +} + +hrtim_timer_rst! { + HRTIM_TIMA: HrCr2: 2, + HRTIM_TIMA: HrCr4: 3, + + HRTIM_TIMB: HrCr2: 2, + HRTIM_TIMB: HrCr4: 3, + + HRTIM_TIMC: HrCr2: 2, + HRTIM_TIMC: HrCr4: 3, + + HRTIM_TIMD: HrCr2: 2, + HRTIM_TIMD: HrCr4: 3, + + HRTIM_TIME: HrCr2: 2, + HRTIM_TIME: HrCr4: 3, + + HRTIM_TIMF: HrCr2: 2, + HRTIM_TIMF: HrCr4: 3 +} diff --git a/src/hrtim/control.rs b/src/hrtim/control.rs new file mode 100644 index 00000000..bcb1414b --- /dev/null +++ b/src/hrtim/control.rs @@ -0,0 +1,368 @@ +use core::marker::PhantomData; + +use crate::{ + hrtim::fault::{ + FltMonitor1, FltMonitor2, FltMonitor3, FltMonitor4, FltMonitor5, FltMonitor6, FltMonitorSys, + }, + rcc::{Enable, Rcc, Reset}, + stm32::{HRTIM_COMMON, RCC}, +}; + +use super::{external_event::EevInputs, fault::FaultInputs}; + +pub trait HrControltExt { + fn hr_control(self, _rcc: &mut Rcc) -> HrTimOngoingCalibration; +} + +impl HrControltExt for HRTIM_COMMON { + fn hr_control(self, _rcc: &mut Rcc) -> HrTimOngoingCalibration { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + unsafe { + let rcc_ptr = &*RCC::ptr(); + + HRTIM_COMMON::enable(rcc_ptr); + HRTIM_COMMON::reset(rcc_ptr); + } + + // Start calibration procedure + common + .dllcr + .write(|w| w.cal().set_bit().calen().clear_bit()); + + HrTimOngoingCalibration { + adc_trigger1_postscaler: AdcTriggerPostscaler::None, + adc_trigger2_postscaler: AdcTriggerPostscaler::None, + adc_trigger3_postscaler: AdcTriggerPostscaler::None, + adc_trigger4_postscaler: AdcTriggerPostscaler::None, + + adc_trigger5_postscaler: AdcTriggerPostscaler::None, + adc_trigger6_postscaler: AdcTriggerPostscaler::None, + adc_trigger7_postscaler: AdcTriggerPostscaler::None, + adc_trigger8_postscaler: AdcTriggerPostscaler::None, + adc_trigger9_postscaler: AdcTriggerPostscaler::None, + adc_trigger10_postscaler: AdcTriggerPostscaler::None, + + flt_divider: SamplingClkDiv::None, + eev_divider: SamplingClkDiv::None, + } + } +} + +pub struct HrTimOngoingCalibration { + adc_trigger1_postscaler: AdcTriggerPostscaler, + adc_trigger2_postscaler: AdcTriggerPostscaler, + adc_trigger3_postscaler: AdcTriggerPostscaler, + adc_trigger4_postscaler: AdcTriggerPostscaler, + + adc_trigger5_postscaler: AdcTriggerPostscaler, + adc_trigger6_postscaler: AdcTriggerPostscaler, + adc_trigger7_postscaler: AdcTriggerPostscaler, + adc_trigger8_postscaler: AdcTriggerPostscaler, + adc_trigger9_postscaler: AdcTriggerPostscaler, + adc_trigger10_postscaler: AdcTriggerPostscaler, + + flt_divider: SamplingClkDiv, + eev_divider: SamplingClkDiv, +} + +impl HrTimOngoingCalibration { + /// SAFETY: Calibration needs to be done before calling this + unsafe fn init(self) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + let Self { + adc_trigger1_postscaler, + adc_trigger2_postscaler, + adc_trigger3_postscaler, + adc_trigger4_postscaler, + + adc_trigger5_postscaler, + adc_trigger6_postscaler, + adc_trigger7_postscaler, + adc_trigger8_postscaler, + adc_trigger9_postscaler, + adc_trigger10_postscaler, + + flt_divider, + eev_divider, + } = self; + + unsafe { + // Enable periodic calibration + // with f_hrtim at 170MHz, these settings leads to + // a period of about 6.2ms + common + .dllcr + .modify(|_r, w| w.calrte().bits(0b00).cal().set_bit().calen().clear_bit()); + common.fltinr2.write(|w| w.fltsd().bits(flt_divider as u8)); + common.eecr3.write(|w| w.eevsd().bits(eev_divider as u8)); + + common.adcps1.write(|w| { + w.adc1psc() + .bits(adc_trigger1_postscaler as u8) + .adc2psc() + .bits(adc_trigger2_postscaler as u8) + .adc3psc() + .bits(adc_trigger3_postscaler as u8) + .adc4psc() + .bits(adc_trigger4_postscaler as u8) + .adc5psc() + .bits(adc_trigger5_postscaler as u8) + }); + + common.adcps2.write(|w| { + w.adc6psc() + .bits(adc_trigger6_postscaler as u8) + .adc7psc() + .bits(adc_trigger7_postscaler as u8) + .adc8psc() + .bits(adc_trigger8_postscaler as u8) + .adc9psc() + .bits(adc_trigger9_postscaler as u8) + .adc10psc() + .bits(adc_trigger10_postscaler as u8) + }); + + // TODO: Adc trigger 5-10 + } + } + + pub fn wait_for_calibration(self) -> (HrTimCalibrated, FaultInputs, EevInputs) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + while common.isr.read().dllrdy().bit_is_clear() { + // Wait until ready + } + + // Calibration is now done, it is safe to continue + unsafe { self.init() }; + + ( + HrTimCalibrated { _x: PhantomData }, + unsafe { FaultInputs::new() }, + unsafe { EevInputs::new() }, + ) + } + + pub fn set_adc1_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger1_postscaler = post_scaler; + self + } + + pub fn set_adc2_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger2_postscaler = post_scaler; + self + } + + pub fn set_adc3_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger3_postscaler = post_scaler; + self + } + + pub fn set_adc4_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger4_postscaler = post_scaler; + self + } + + pub fn set_fault_sampling_division(mut self, divider: SamplingClkDiv) -> Self { + self.flt_divider = divider; + self + } + + pub fn set_eev_sampling_division(mut self, divider: SamplingClkDiv) -> Self { + self.eev_divider = divider; + self + } +} + +/// This object may be used for things that needs to be done before any timers have been started but after the calibration has been completed. Its existence is proof that no timers have started. +/// +/// Once done with setup, use the `constrain` to get a `HrPwmControl` which can be used to start the timers. +pub struct HrTimCalibrated { + _x: PhantomData<()>, +} + +impl HrTimCalibrated { + pub fn constrain(self) -> HrPwmControl { + HrPwmControl { + _x: PhantomData, + fault_sys: FltMonitorSys { _x: PhantomData }, + fault_1: FltMonitor1 { _x: PhantomData }, + fault_2: FltMonitor2 { _x: PhantomData }, + fault_3: FltMonitor3 { _x: PhantomData }, + fault_4: FltMonitor4 { _x: PhantomData }, + fault_5: FltMonitor5 { _x: PhantomData }, + fault_6: FltMonitor6 { _x: PhantomData }, + + adc_trigger1: Adc1Trigger { _x: PhantomData }, + adc_trigger2: Adc2Trigger { _x: PhantomData }, + adc_trigger3: Adc3Trigger { _x: PhantomData }, + adc_trigger4: Adc4Trigger { _x: PhantomData }, + adc_trigger5: Adc5Trigger { _x: PhantomData }, + adc_trigger6: Adc6Trigger { _x: PhantomData }, + adc_trigger7: Adc7Trigger { _x: PhantomData }, + adc_trigger8: Adc8Trigger { _x: PhantomData }, + adc_trigger9: Adc9Trigger { _x: PhantomData }, + adc_trigger10: Adc10Trigger { _x: PhantomData }, + } + } +} + +pub struct HrPwmControl { + _x: PhantomData<()>, + + pub fault_sys: FltMonitorSys, + pub fault_1: FltMonitor1, + pub fault_2: FltMonitor2, + pub fault_3: FltMonitor3, + pub fault_4: FltMonitor4, + pub fault_5: FltMonitor5, + pub fault_6: FltMonitor6, + + pub adc_trigger1: Adc1Trigger, + pub adc_trigger2: Adc2Trigger, + pub adc_trigger3: Adc3Trigger, + pub adc_trigger4: Adc4Trigger, + + pub adc_trigger5: Adc5Trigger, + pub adc_trigger6: Adc6Trigger, + pub adc_trigger7: Adc7Trigger, + pub adc_trigger8: Adc8Trigger, + pub adc_trigger9: Adc9Trigger, + pub adc_trigger10: Adc10Trigger, +} + +macro_rules! impl_adc1234_trigger { + ($($t:ident: [$trait_:ident, $adcXr:ident, $variant345:ident $(, $variant12:ident)*]),*) => {$( + pub struct $t { + _x: PhantomData<()>, + } + + impl $t { + pub fn enable_source(&mut self, _trigger: &T) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + unsafe { + common.$adcXr.modify(|r, w| w.bits(r.bits() | T::BITS)); + } + } + } + + $(impl From<&$t> for crate::adc::config::ExternalTrigger12 { + fn from(_val: &$t) -> Self { + crate::adc::config::ExternalTrigger12::$variant12 + } + })* + + impl From<&$t> for crate::adc::config::ExternalTrigger345 { + fn from(_val: &$t) -> Self { + crate::adc::config::ExternalTrigger345::$variant345 + } + } + )*} +} + +macro_rules! impl_adc5678910_trigger { + ($($t:ident: [$trait_:ident, $adcXtrg:ident, $variant345:ident, $variant12:ident]),*) => {$( + pub struct $t { + _x: PhantomData<()>, + } + + impl $t { + pub fn enable_source(&mut self, _trigger: &T) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common + .adcer + .modify(|_r, w| w.$adcXtrg().variant(T::BITS as u8)); + } + } + + impl From<&$t> for crate::adc::config::ExternalTrigger12 { + fn from(_val: &$t) -> Self { + crate::adc::config::ExternalTrigger12::$variant12 + } + } + + impl From<&$t> for crate::adc::config::ExternalTrigger345 { + fn from(_val: &$t) -> Self { + crate::adc::config::ExternalTrigger345::$variant345 + } + } + + )*} +} + +impl_adc1234_trigger! {// reg adc345, adc12 + Adc1Trigger: [Adc13Trigger, adc1r, Hrtim_adc_trg_1, Hrtim_adc_trg_1], + Adc2Trigger: [Adc24Trigger, adc2r, Hrtim_adc_trg_2], + Adc3Trigger: [Adc13Trigger, adc3r, Hrtim_adc_trg_3, Hrtim_adc_trg_3], + Adc4Trigger: [Adc24Trigger, adc4r, Hrtim_adc_trg_4] +} + +impl_adc5678910_trigger! { + Adc5Trigger: [Adc579Trigger, adc5trg, Hrtim_adc_trg_5, Hrtim_adc_trg_5], + Adc6Trigger: [Adc6810Trigger, adc6trg, Hrtim_adc_trg_6, Hrtim_adc_trg_6], + Adc7Trigger: [Adc579Trigger, adc7trg, Hrtim_adc_trg_7, Hrtim_adc_trg_7], + Adc8Trigger: [Adc6810Trigger, adc8trg, Hrtim_adc_trg_8, Hrtim_adc_trg_8], + Adc9Trigger: [Adc579Trigger, adc9trg, Hrtim_adc_trg_9, Hrtim_adc_trg_9], + Adc10Trigger: [Adc6810Trigger, adc10trg, Hrtim_adc_trg_10, Hrtim_adc_trg_10] +} + +use super::adc_trigger::{Adc13Trigger, Adc24Trigger, Adc579Trigger, Adc6810Trigger}; + +pub enum AdcTriggerPostscaler { + None = 0, + Div2 = 1, + Div3 = 2, + Div4 = 3, + Div5 = 4, + Div6 = 5, + Div7 = 6, + Div8 = 7, + Div9 = 8, + Div10 = 9, + Div11 = 10, + Div12 = 11, + Div13 = 12, + Div14 = 13, + Div15 = 14, + Div16 = 15, + Div17 = 16, + Div18 = 17, + Div19 = 18, + Div20 = 19, + Div21 = 20, + Div22 = 21, + Div23 = 22, + Div24 = 23, + Div25 = 24, + Div26 = 25, + Div27 = 26, + Div28 = 27, + Div29 = 28, + Div30 = 29, + Div31 = 30, + Div32 = 31, +} + +/// The divsion ratio between f_hrtim and the fault signal sampling clock for digital filters +pub enum SamplingClkDiv { + /// No division + /// + /// fault signal sampling clock f_flts = f_hrtim + None = 0b00, + + /// 1/2 + /// + /// fault signal sampling clock f_flts = f_hrtim / 2 + Two = 0b01, + + /// 1/4 + /// + /// fault signal sampling clock f_flts = f_hrtim / 4 + Four = 0b10, + + /// 1/8 + /// + /// fault signal sampling clock f_flts = f_hrtim / 8 + Eight = 0b11, +} diff --git a/src/hrtim/deadtime.rs b/src/hrtim/deadtime.rs new file mode 100644 index 00000000..c59c0641 --- /dev/null +++ b/src/hrtim/deadtime.rs @@ -0,0 +1,79 @@ +#[derive(Copy, Clone, Debug)] +pub struct DeadtimeConfig { + /// Prescaler for both rising and falling deadtime + pub(crate) prescaler: DeadtimePrescaler, + + /// 9-bits + pub(crate) deadtime_rising_value: u16, + + /// Is deadtime negative + pub(crate) deadtime_rising_sign: bool, + + /// 9-bits + pub(crate) deadtime_falling_value: u16, + + /// Is deadtime negative + pub(crate) deadtime_falling_sign: bool, +} + +impl DeadtimeConfig { + /// See RM0440 Table 221 'Deadtime resolution and max absolute values' + pub fn prescaler(mut self, value: DeadtimePrescaler) -> Self { + self.prescaler = value; + self + } + + /// Panic if value can not fit in 9 bits + pub fn deadtime_rising_value(mut self, value: u16) -> Self { + // 9 bits + assert!(value < (1 << 9)); + + self.deadtime_rising_value = value; + + self + } + + pub fn deadtime_rising_sign(mut self, is_negative: bool) -> Self { + self.deadtime_rising_sign = is_negative; + self + } + + /// Panic if value can not fit in 9 bits + pub fn deadtime_falling_value(mut self, value: u16) -> Self { + // 9 bits + assert!(value < (1 << 9)); + + self.deadtime_falling_value = value; + + self + } + + pub fn deadtime_falling_sign(mut self, is_negative: bool) -> Self { + self.deadtime_falling_sign = is_negative; + self + } +} + +impl Default for DeadtimeConfig { + fn default() -> Self { + Self { + prescaler: DeadtimePrescaler::Thrtim, + deadtime_rising_value: 170, // about 1us when f_sys = 170MHz + deadtime_rising_sign: false, + deadtime_falling_value: 170, // about 1us when f_sys = 170MHz + deadtime_falling_sign: false, + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum DeadtimePrescaler { + ThrtimDiv8 = 0b000, + ThrtimDiv4 = 0b001, + ThrtimDiv2 = 0b010, + Thrtim = 0b011, + ThrtimMul2 = 0b100, + ThrtimMul4 = 0b101, + ThrtimMul8 = 0b110, + ThrtimMul16 = 0b111, +} diff --git a/src/hrtim/event.rs b/src/hrtim/event.rs new file mode 100644 index 00000000..5c472809 --- /dev/null +++ b/src/hrtim/event.rs @@ -0,0 +1,16 @@ +/// Event that can be used to set/reset an output +pub trait EventSource { + const BITS: u32; +} + +/// Done: +/// * [x] Eev1-10 +/// * [x] Master period +/// * [x] Master CMP1-4 +/// * [x] Cmp2, Cmp4 +/// * [x] Timer Update +/// * [ ] Neighbor timers compare events +/// Event that can be used reset the timer counter +pub trait TimerResetEventSource { + const BITS: u32; +} diff --git a/src/hrtim/external_event.rs b/src/hrtim/external_event.rs new file mode 100644 index 00000000..2389dc3a --- /dev/null +++ b/src/hrtim/external_event.rs @@ -0,0 +1,376 @@ +use core::marker::PhantomData; + +use crate::comparator::{COMP1, COMP2, COMP3, COMP4, COMP5, COMP6, COMP7}; +use crate::gpio::gpiob::{PB3, PB4, PB5, PB6, PB7, PB8, PB9}; +use crate::gpio::gpioc::{PC11, PC12, PC5, PC6}; +use crate::gpio::{self, AF13, AF3}; +use crate::pwm::Polarity; +use crate::stm32::HRTIM_COMMON; + +use super::control::HrTimCalibrated; + +#[derive(Copy, Clone, PartialEq)] +pub struct ExternalEventSource { + _x: PhantomData<()>, +} + +pub struct EevInputs { + pub eev_input1: EevInput<1>, + pub eev_input2: EevInput<2>, + pub eev_input3: EevInput<3>, + pub eev_input4: EevInput<4>, + pub eev_input5: EevInput<5>, + pub eev_input6: EevInput<6>, + pub eev_input7: EevInput<7>, + pub eev_input8: EevInput<8>, + pub eev_input9: EevInput<9>, + pub eev_input10: EevInput<10>, +} + +impl EevInputs { + pub(crate) unsafe fn new() -> Self { + EevInputs { + eev_input1: EevInput { _x: PhantomData }, + eev_input2: EevInput { _x: PhantomData }, + eev_input3: EevInput { _x: PhantomData }, + eev_input4: EevInput { _x: PhantomData }, + eev_input5: EevInput { _x: PhantomData }, + eev_input6: EevInput { _x: PhantomData }, + eev_input7: EevInput { _x: PhantomData }, + eev_input8: EevInput { _x: PhantomData }, + eev_input9: EevInput { _x: PhantomData }, + eev_input10: EevInput { _x: PhantomData }, + } + } +} + +pub struct EevInput { + _x: PhantomData<()>, +} + +/// This is implemented for types that can be used as inputs to the eev +/// # Safety +/// Only implement for types that can be used as sources to eev number `EEV_N` with src bits `SRC_BITS` +pub unsafe trait EevSrcBits: Sized { + const SRC_BITS: u8; + fn cfg(self) {} +} + +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<$compX, ED> + where ED: crate::comparator::EnabledState + { + const SRC_BITS: u8 = 0b01; + } + + $( + unsafe impl EevSrcBits<$N> for &crate::comparator::Comparator<$compY, ED> + where ED: crate::comparator::EnabledState + { + const SRC_BITS: u8 = $compY_src_bits; + } + )* + + impl EevInput<$N> { + pub 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)]); +impl_eev_input!(2: COMP = [COMP4], PINS = [(PC11, AF3)]); +impl_eev_input!(3: COMP = [COMP6], PINS = [(PB7, AF13)]); +impl_eev_input!(4: COMP = [COMP1, (COMP5, 0b10)], PINS = [(PB6, AF13)]); +impl_eev_input!(5: COMP = [COMP3, (COMP7, 0b10)], PINS = [(PB9, AF13)]); +impl_eev_input!(6: COMP = [COMP2, (COMP1, 0b10)], PINS = [(PB5, AF13)]); +impl_eev_input!(7: COMP = [COMP4], PINS = [(PB4, AF13)]); +impl_eev_input!(8: COMP = [COMP6, (COMP3, 0b10)], PINS = [(PB8, AF13)]); +impl_eev_input!(9: COMP = [COMP5, (COMP4, 0b11)], PINS = [(PB3, AF13)]); +impl_eev_input!(10: COMP = [COMP7], PINS = [(PC5, AF13), (PC6, AF3)]); + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum EdgeOrPolarity { + Edge(Edge), + Polarity(Polarity), +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Edge { + Rising = 0b01, + Falling = 0b10, + Both = 0b11, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum EevSamplingFilter { + /// No filtering, fault acts asynchronously + /// + /// Note that this bypasses any f_eevs (FaultSamplingClkDiv) + None = 0b0000, + + /// Sample directly at rate f_hrtim, with a count of 2 + /// + /// Note that this bypasses: any f_eevs (FaultSamplingClkDiv) + HrtimN2 = 0b0001, + + /// Sample directly at rate f_hrtim, with a count of 4 + /// + /// Note that this bypasses any f_eevs (FaultSamplingClkDiv) + HrtimN4 = 0b0010, + + /// Sample directly at rate f_hrtim, with a count of 8 + /// + /// Note that this bypasses any f_eevs (FaultSamplingClkDiv) + HrtimN8 = 0b0011, + + /// Sample at rate f_eevs / 2, with a count of 6 + EevsDiv2N6 = 0b0100, + + /// Sample at rate f_eevs / 2, with a count of 8 + EevsDiv2N8 = 0b0101, + + /// Sample at rate f_eevs / 4, with a count of 6 + EevsDiv4N6 = 0b0110, + + /// Sample at rate f_eevs / 4, with a count of 8 + EevsDiv4N8 = 0b0111, + + /// Sample at rate f_eevs / 8, with a count of 6 + EevsDiv8N6 = 0b1000, + + /// Sample at rate f_eevs / 8, with a count of 8 + EevsDiv8N8 = 0b1001, + + /// Sample at rate f_eevs / 16, with a count of 5 + EevsDiv16N5 = 0b1010, + + /// Sample at rate f_eevs / 16, with a count of 6 + EevsDiv16N6 = 0b1011, + + /// Sample at rate f_eevs / 16, with a count of 8 + EevsDiv16N8 = 0b1100, + + /// Sample at rate f_eevs / 32, with a count of 5 + EevsDiv32N5 = 0b1101, + + /// Sample at rate f_eevs / 32, with a count of 6 + EevsDiv32N6 = 0b1110, + + /// Sample at rate f_eevs / 32, with a count of 8 + EevsDiv32N8 = 0b1111, +} + +pub trait ExternalEventBuilder1To5 {} +pub trait ExternalEventBuilder6To10 {} +pub struct SourceBuilder { + /// EExSRC + src_bits: u8, + + /// EExSNS + edge_or_polarity_bits: u8, + + /// EExPOL + polarity_bit: bool, + + /// EExF + filter_bits: u8, +} + +impl SourceBuilder { + unsafe fn new(src_bits: u8) -> Self { + Self { + src_bits, + edge_or_polarity_bits: 0, // Level sensitive + polarity_bit: false, // Active high + filter_bits: 0, // No filter + } + } +} + +impl SourceBuilder { + pub fn edge_or_polarity(mut self, edge_or_polarity: EdgeOrPolarity) -> Self { + (self.edge_or_polarity_bits, self.polarity_bit) = match edge_or_polarity { + EdgeOrPolarity::Polarity(Polarity::ActiveHigh) => (0b00, false), + EdgeOrPolarity::Polarity(Polarity::ActiveLow) => (0b00, true), + EdgeOrPolarity::Edge(Edge::Rising) => (0b01, false), + EdgeOrPolarity::Edge(Edge::Falling) => (0b10, false), + EdgeOrPolarity::Edge(Edge::Both) => (0b11, false), + }; + + self + } +} + +impl SourceBuilder { + /// Edge sensitivity not available in fast mode + pub fn polarity(mut self, polarity: Polarity) -> Self { + (self.edge_or_polarity_bits, self.polarity_bit) = match polarity { + Polarity::ActiveHigh => (0b00, false), + Polarity::ActiveLow => (0b00, true), + }; + + self + } +} + +impl SourceBuilder +where + SourceBuilder: ExternalEventBuilder6To10, +{ + pub fn filter(mut self, filter: EevSamplingFilter) -> Self { + self.filter_bits = filter as _; + self + } +} + +pub trait ToExternalEventSource { + fn finalize(self, _calibrated: &mut HrTimCalibrated) -> ExternalEventSource; +} + +#[derive(Copy, Clone)] +struct ExternalEventMuxOut { + _x: PhantomData<()>, +} + +macro_rules! impl_eev1_5_to_es { + ($eev:ident, $N:literal, $eeXsrc:ident, $eeXpol:ident, $eeXsns:ident, $eeXfast:ident) => { + impl ExternalEventBuilder1To5 for SourceBuilder<$N, IS_FAST> {} + + impl SourceBuilder<$N, false> { + pub fn fast(self) -> SourceBuilder<$N, true> { + let SourceBuilder { + src_bits, + edge_or_polarity_bits, + polarity_bit, + filter_bits, + } = self; + + SourceBuilder { + src_bits, + edge_or_polarity_bits, + polarity_bit, + filter_bits, + } + } + } + + impl ToExternalEventSource<$N, IS_FAST> + for SourceBuilder<$N, IS_FAST> + { + fn finalize( + self, + _calibrated: &mut HrTimCalibrated, + ) -> ExternalEventSource<$N, IS_FAST> { + let SourceBuilder { + src_bits, + edge_or_polarity_bits, + polarity_bit, + filter_bits: _, + } = self; + + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + // SAFETY: Thanks to, `HrTimCalibrated`, we know we have exclusive access to the register, + // we also know no timers are started. + unsafe { + common.eecr1.modify(|_r, w| { + w.$eeXsrc() + .bits(src_bits) + .$eeXpol() + .bit(polarity_bit) + .$eeXsns() + .bits(edge_or_polarity_bits) + .$eeXfast() + .bit(IS_FAST) + }); + } + + ExternalEventSource { _x: PhantomData } + } + } + + /// EEV$1 event + impl super::event::EventSource + for ExternalEventSource<$N, IS_FAST> + { + const BITS: u32 = 1 << ($N + 20); // EEV1 is at bit 21 + } + }; +} + +macro_rules! impl_eev6_10_to_es { + ($eev:ident, $N:literal, $eeXsrc:ident, $eeXpol:ident, $eeXsns:ident, $eeXf:ident) => { + impl ExternalEventBuilder6To10 for SourceBuilder<$N, false> {} + + impl ToExternalEventSource<$N, false> for SourceBuilder<$N, false> { + fn finalize(self, _calibrated: &mut HrTimCalibrated) -> ExternalEventSource<$N, false> { + let SourceBuilder { + src_bits, + edge_or_polarity_bits, + polarity_bit, + filter_bits, + } = self; + + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + // SAFETY: Thanks to, `HrTimCalibrated`, we know we have exclusive access to the register, + // we also know no timers are started. + unsafe { + common.eecr2.modify(|_r, w| { + w.$eeXsrc() + .bits(src_bits) + .$eeXpol() + .bit(polarity_bit) + .$eeXsns() + .bits(edge_or_polarity_bits) + }); + common.eecr3.modify(|_r, w| w.$eeXf().bits(filter_bits)); + } + + ExternalEventSource { _x: PhantomData } + } + } + + /// EEV$1 event + impl super::event::EventSource for ExternalEventSource<$N, false> { + const BITS: u32 = 1 << ($N + 20); // EEV1 is at bit 21 + } + }; +} + +impl_eev1_5_to_es!(Eevnt1, 1, ee1src, ee1pol, ee1sns, ee1fast); +impl_eev1_5_to_es!(Eevnt2, 2, ee2src, ee2pol, ee2sns, ee2fast); +impl_eev1_5_to_es!(Eevnt3, 3, ee3src, ee3pol, ee3sns, ee3fast); +impl_eev1_5_to_es!(Eevnt4, 4, ee4src, ee4pol, ee4sns, ee4fast); +impl_eev1_5_to_es!(Eevnt5, 5, ee5src, ee5pol, ee5sns, ee5fast); + +impl_eev6_10_to_es!(Eevnt6, 6, ee6src, ee6pol, ee6sns, ee6f); +impl_eev6_10_to_es!(Eevnt7, 7, ee7src, ee7pol, ee7sns, ee7f); +impl_eev6_10_to_es!(Eevnt8, 8, ee8src, ee8pol, ee8sns, ee8f); +impl_eev6_10_to_es!(Eevnt9, 9, ee9src, ee9pol, ee9sns, ee9f); +impl_eev6_10_to_es!(Eevnt10, 10, ee10src, ee10pol, ee10sns, ee10f); + +impl super::capture::CaptureEvent + for ExternalEventSource +{ + const BITS: u32 = 1 << (N + 1); // EEV1 is at bit #2 etc +} + +impl super::event::TimerResetEventSource + for ExternalEventSource +{ + const BITS: u32 = 1 << (N + 8); // EEV1 is at bit 9 +} diff --git a/src/hrtim/fault.rs b/src/hrtim/fault.rs new file mode 100644 index 00000000..17fdd34d --- /dev/null +++ b/src/hrtim/fault.rs @@ -0,0 +1,262 @@ +use core::marker::PhantomData; + +use crate::comparator::{COMP1, COMP2, COMP3, COMP4, COMP5, COMP6}; +use crate::gpio::gpioa::{PA12, PA15}; +use crate::gpio::gpiob::{PB0, PB10, PB11}; +use crate::gpio::gpioc::{PC10, PC7}; +use crate::gpio::{self, AF13, AF3}; +use crate::hrtim::control::HrPwmControl; +use crate::pwm::FaultMonitor; +use crate::stm32::HRTIM_COMMON; + +pub enum FaultAction { + /// Output never enters fault mode + None = 0b00, + + /// Output forced to `active` level on fault + ForceActive = 0b01, + + /// Output forced to `inactive` level on fault + ForceInactive = 0b10, + + /// The output is floating/tri stated on fault + Floating = 0b11, +} + +/// # Safety +/// Only implement for actual fault sources with correct `ENABLE_BITS` +pub unsafe trait FaultSource: Copy { + const ENABLE_BITS: u8; +} + +pub struct SourceBuilder { + _input: I, + src_bits: u8, + + /// FLTxP + is_active_high: bool, + + /// FLTxF[3:0] + filter_bits: u8, +} + +impl SourceBuilder { + unsafe fn new(input: I, src_bits: u8) -> Self { + SourceBuilder { + _input: input, + src_bits, + is_active_high: false, + filter_bits: 0b0000, + } + } +} + +macro_rules! impl_faults { + ($( + $input:ident => $source:ident: + PINS=[($pin:ident, $af:ident) $(,($pin_b:ident, $af_b:ident))*], + COMP=$compX:ident, $enable_bits:literal, + $fltinrZ:ident, $fltWsrc_0:ident, $fltWsrc_1:ident, $fltWp:ident, $fltWf:ident, $fltWe:ident, $fltWlck:ident, + )+) => {$( + + // This should NOT be Copy/Clone + pub struct $input { + pub(crate) _x: PhantomData<()> + } + + #[derive(Copy, Clone)] + pub struct $source { + _x: PhantomData<()> + } + + impl $input { + pub fn bind_pin(self, pin: $pin>) -> SourceBuilder<$input> { + pin.into_alternate::<$af>(); + unsafe { SourceBuilder::new(self, 0b00) } + } + + $( + // TODO: Is there a nicer way to do this? + pub fn bind_pin_b(self, pin: $pin_b>) -> SourceBuilder<$input> { + pin.into_alternate::<$af_b>(); + unsafe { SourceBuilder::new(self, 0b00) } + } + )* + + pub fn bind_comp(self, _comp: &crate::comparator::Comparator<$compX, crate::comparator::Enabled>) -> SourceBuilder<$input> { + unsafe { SourceBuilder::new(self, 0b01) } + } + + /*pub fn bind_external(?) { + SourceBuilder::new(self, 0b10); + }*/ + } + + impl SourceBuilder<$input> { + pub fn finalize(self, _control: &mut HrPwmControl) -> $source { + let SourceBuilder{ _input, src_bits, is_active_high, filter_bits } = self; + + // Setup fault source + unsafe { + let common = &*HRTIM_COMMON::ptr(); + + common.fltinr2.modify(|_r, w| w.$fltWsrc_1().bit(src_bits & 0b10 != 0)); + common.$fltinrZ.modify(|_r, w| w + .$fltWsrc_0().bit(src_bits & 0b01 != 0) + .$fltWp().bit(is_active_high) + .$fltWf().bits(filter_bits) + .$fltWe().set_bit() // Enable + ); + + // ... and lock configuration + common.$fltinrZ.modify(|_r, w| w.$fltWlck().set_bit()); + } + + $source { + _x: PhantomData + } + } + + pub fn polarity(mut self, polarity: super::Polarity) -> Self { + self.is_active_high = polarity == super::Polarity::ActiveHigh; + self + } + + // TODO: add more settings + /* pub fn blanking(?) -> Self */ + + pub fn filter(mut self, filter: FaultSamplingFilter) -> Self { + self.filter_bits = filter as u8; + self + } + } + + unsafe impl FaultSource for $source { + const ENABLE_BITS: u8 = $enable_bits; + } + )+} +} + +impl_faults!( + FaultInput1 => FaultSource1: PINS=[(PA12, AF13)], COMP=COMP2, 0b000001, fltinr1, flt1src, flt1src_1, flt1p, flt1f, flt1e, flt1lck, + FaultInput2 => FaultSource2: PINS=[(PA15, AF13)], COMP=COMP4, 0b000010, fltinr1, flt2src, flt2src_1, flt2p, flt2f, flt2e, flt2lck, + FaultInput3 => FaultSource3: PINS=[(PB10, AF13)], COMP=COMP6, 0b000100, fltinr1, flt3src, flt3src_1, flt3p, flt3f, flt3e, flt3lck, + FaultInput4 => FaultSource4: PINS=[(PB11, AF13)], COMP=COMP1, 0b001000, fltinr1, flt4src, flt4src_1, flt4p, flt4f, flt4e, flt4lck, + FaultInput5 => FaultSource5: PINS=[(PB0, AF13), (PC7, AF3)], COMP=COMP3, 0b010000, fltinr2, flt5src, flt5src_1, flt5p, flt5f, flt5e, flt5lck, + FaultInput6 => FaultSource6: PINS=[(PC10, AF13)], COMP=COMP5, 0b100000, fltinr2, flt6src_0, flt6src_1, flt6p, flt6f, flt6e, flt6lck, +); + +pub struct FaultInputs { + pub fault_input1: FaultInput1, + pub fault_input2: FaultInput2, + pub fault_input3: FaultInput3, + pub fault_input4: FaultInput4, + pub fault_input5: FaultInput5, + pub fault_input6: FaultInput6, +} + +impl FaultInputs { + pub(crate) unsafe fn new() -> Self { + FaultInputs { + fault_input1: FaultInput1 { _x: PhantomData }, + fault_input2: FaultInput2 { _x: PhantomData }, + fault_input3: FaultInput3 { _x: PhantomData }, + fault_input4: FaultInput4 { _x: PhantomData }, + fault_input5: FaultInput5 { _x: PhantomData }, + fault_input6: FaultInput6 { _x: PhantomData }, + } + } +} + +pub enum FaultSamplingFilter { + /// No filtering, fault acts asynchronously + /// + /// Note that this bypasses any f_flts (SamplingClkDiv) + None = 0b0000, + + /// Sample directly at rate f_hrtim, with a count of 2 + /// + /// Note that this bypasses: any f_flts (SamplingClkDiv) + HrtimN2 = 0b0001, + + /// Sample directly at rate f_hrtim, with a count of 4 + /// + /// Note that this bypasses any f_flts (SamplingClkDiv) + HrtimN4 = 0b0010, + + /// Sample directly at rate f_hrtim, with a count of 8 + /// + /// Note that this bypasses any f_flts (SamplingClkDiv) + HrtimN8 = 0b0011, + + /// Sample at rate f_flts / 2, with a count of 6 + FltsDiv2N6 = 0b0100, + + /// Sample at rate f_flts / 2, with a count of 8 + FltsDiv2N8 = 0b0101, + + /// Sample at rate f_flts / 4, with a count of 6 + FltsDiv4N6 = 0b0110, + + /// Sample at rate f_flts / 4, with a count of 8 + FltsDiv4N8 = 0b0111, + + /// Sample at rate f_flts / 8, with a count of 6 + FltsDiv8N6 = 0b1000, + + /// Sample at rate f_flts / 8, with a count of 8 + FltsDiv8N8 = 0b1001, + + /// Sample at rate f_flts / 16, with a count of 5 + FltsDiv16N5 = 0b1010, + + /// Sample at rate f_flts / 16, with a count of 6 + FltsDiv16N6 = 0b1011, + + /// Sample at rate f_flts / 16, with a count of 8 + FltsDiv16N8 = 0b1100, + + /// Sample at rate f_flts / 32, with a count of 5 + FltsDiv32N5 = 0b1101, + + /// Sample at rate f_flts / 32, with a count of 6 + FltsDiv32N6 = 0b1110, + + /// Sample at rate f_flts / 32, with a count of 8 + FltsDiv32N8 = 0b1111, +} + +macro_rules! impl_flt_monitor { + ($($t:ident: ($fltx:ident, $fltxc:ident),)+) => {$( + pub struct $t { + pub(crate) _x: PhantomData<()> + } + + impl FaultMonitor for $t { + fn is_fault_active(&self) -> bool { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.isr.read().$fltx().bit() + } + + fn clear_fault(&mut self) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.icr.write(|w| w.$fltxc().set_bit()); + } + + // TODO: Should we have our own trait since it does not seem possible to implement this + fn set_fault(&mut self) { + todo!() + } + } + )+}; +} + +impl_flt_monitor!( + FltMonitorSys: (sysflt, sysfltc), + FltMonitor1: (flt1, flt1c), + FltMonitor2: (flt2, flt2c), + FltMonitor3: (flt3, flt3c), + FltMonitor4: (flt4, flt4c), + FltMonitor5: (flt5, flt5c), + FltMonitor6: (flt6, flt6c), +); diff --git a/src/hrtim/mod.rs b/src/hrtim/mod.rs new file mode 100644 index 00000000..92fbc73f --- /dev/null +++ b/src/hrtim/mod.rs @@ -0,0 +1,953 @@ +pub mod adc_trigger; +pub mod capture; +pub mod compare_register; +pub mod control; +pub mod deadtime; +pub mod event; +pub mod external_event; +pub mod fault; +pub mod output; +pub mod timer; +pub mod timer_eev_cfg; + +use core::marker::PhantomData; +use core::mem::MaybeUninit; + +use crate::hrtim::capture::HrCapt; +use crate::hrtim::compare_register::{HrCr1, HrCr2, HrCr3, HrCr4}; +use crate::hrtim::fault::{FaultAction, FaultSource}; +use crate::hrtim::timer::HrTim; +use crate::stm32::{ + HRTIM_COMMON, HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, + HRTIM_TIMF, +}; +use fugit::HertzU64; + +use self::control::HrPwmControl; + +use self::deadtime::DeadtimeConfig; +use self::output::{HrtimChannel, ToHrOut, CH1, CH2}; +use self::timer_eev_cfg::EevCfgs; + +use crate::pwm::{ + self, Alignment, ComplementaryImpossible, Pins, Polarity, Pwm, PwmPinEnable, TimerType, +}; +use crate::rcc::{GetBusFreq, Rcc}; +use crate::time::Hertz; + +/// Internal enum that keeps track of the count settings before PWM is finalized +enum CountSettings { + Frequency(Hertz), + Period(u16), +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum HrTimerMode { + SingleShotNonRetriggerable, + SingleShotRetriggerable, + Continuous, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum HrCountingDirection { + /// Asymetrical up counting mode + /// + /// + + /// * * + /// Counting up * | * | + /// * * + /// * | * | + /// * * + /// * | * | + /// * * + /// -------------------------------------- + /// + /// ```txt + /// | *-------* *------ + /// | | | + /// | | | | + /// | | | + /// ----------* *------------------* + /// ``` + /// + /// This is the most common mode with least amount of quirks + Up, + + /// Symmetrical up-down counting mode + /// + /// + /// ```txt + /// Period--> * Counting * + /// Counting up * | * Counting Up * | + /// * * down * + /// * | * * | + /// * * * + /// * | * * | + /// 0 -->* * + /// --------------------------------------------------------------------------- + /// | *---------------* | *---------------* + /// | | | | | | + /// | | | | | | + /// | | | | | | + /// ----------* *-------------------* *--- + /// ``` + /// + /// NOTE: This is incompatible with + /// * Auto-delay + /// * Balanded Idle + /// * Triggered-half mode + /// + /// There is also differences in (including but not limited to) the following areas: + /// * Counter roll over event + /// * The events registered with `enable_set_event` will work as normal wen counting up, however when counting down, they will work as rst events. + /// * The events registered with `enable_rst_event` will work as normal wen counting up, however when counting down, they will work as set events. + UpDown, +} + +// Needed to calculate frequency +impl From for pwm::Alignment { + fn from(val: HrCountingDirection) -> Self { + match val { + HrCountingDirection::Up => pwm::Alignment::Left, + HrCountingDirection::UpDown => pwm::Alignment::Center, + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum InterleavedMode { + Disabled, + + /// Dual interleaved or Half mode + /// + /// Automatically force + /// * Cr1 to PERIOD / 2 (not visable through `get_duty`). + /// Automatically updates when changing period + /// + /// NOTE: Affects Cr1 + Dual, + + /// Triple interleaved mode + /// + /// Automatically force + /// * Cr1 to 1 * PERIOD / 3 and + /// * Cr2 to 2 * PERIOD / 3 + /// (not visable through `get_duty`). Automatically updates when changing period. + /// + /// NOTE: Must not be used simultaneously with other modes + /// using CMP2 (dual channel dac trigger and triggered-half modes). + Triple, + + /// Quad interleaved mode + /// + /// Automatically force + /// * Cr1 to 1 * PERIOD / 4, + /// * Cr2 to 2 * PERIOD / 4 and + /// * Cr3 to 3 * PERIOD / 4 + /// (not visable through `get_duty`). Automatically updates when changing period. + /// + /// NOTE: Must not be used simultaneously with other modes + /// using CMP2 (dual channel dac trigger and triggered-half modes). + Quad, +} + +// HrPwmExt trait +/// Allows the pwm() method to be added to the peripheral register structs from the device crate +pub trait HrPwmExt: Sized { + /// The requested frequency will be rounded to the nearest achievable frequency; the actual frequency may be higher or lower than requested. + fn pwm( + self, + _pins: PINS, + frequency: T, + control: &mut HrPwmControl, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins + ToHrOut, + T: Into, + U: HrtimChannel; +} + +pub trait HrPwmAdvExt: Sized { + type PreloadSource; + + fn pwm_advanced( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder> + where + PINS: Pins + ToHrOut, + CHANNEL: HrtimChannel; +} + +/// HrPwmBuilder is used to configure advanced HrTim PWM features +pub struct HrPwmBuilder { + _tim: PhantomData, + _prescaler: PhantomData, + _out: PhantomData, + timer_mode: HrTimerMode, + counting_direction: HrCountingDirection, + base_freq: HertzU64, + count: CountSettings, + preload_source: Option, + fault_enable_bits: u8, + fault1_bits: u8, + fault2_bits: u8, + enable_push_pull: bool, + interleaved_mode: InterleavedMode, // Also includes half mode + repetition_counter: u8, + deadtime: Option, + enable_repetition_interrupt: bool, + eev_cfg: EevCfgs, + out1_polarity: Polarity, + out2_polarity: Polarity, +} + +pub enum PreloadSource { + /// Preloaded registers are updated on counter roll over or counter reset + OnCounterReset, + + /// Preloaded registers are updated by master timer update + OnMasterTimerUpdate, + + /// Prealoaded registers are updaten when the counter rolls over and the repetition counter is 0 + OnRepetitionUpdate, +} + +pub enum MasterPreloadSource { + /// Prealoaded registers are updaten when the master counter rolls over and the master repetition counter is 0 + OnMasterRepetitionUpdate, +} + +macro_rules! hrtim_finalize_body { + ($this:expr, $PreloadSource:ident, $TIMX:ident: ( + $timXcr:ident, $ck_psc:ident, $perXr:ident, $perx:ident, $tXcen:ident, $rep:ident, $repx:ident, $dier:ident, $repie:ident + $(, $timXcr2:ident, $fltXr:ident, $eefXr1:ident, $eefXr2:ident, $Xeefr3:ident, $outXr:ident, $dtXr:ident)*), + ) => {{ + let tim = unsafe { &*$TIMX::ptr() }; + let (period, prescaler_bits) = match $this.count { + CountSettings::Period(period) => (period as u32, PSCL::BITS as u16), + CountSettings::Frequency( freq ) => { + >::calculate_frequency($this.base_freq, freq, $this.counting_direction.into()) + }, + }; + + let (half, intlvd) = match $this.interleaved_mode { + InterleavedMode::Disabled => (false, 0b00), + InterleavedMode::Dual => (true, 0b00), + InterleavedMode::Triple => (false, 0b01), + InterleavedMode::Quad => (false, 0b10), + }; + + // Write prescaler and any special modes + tim.$timXcr.modify(|_r, w| unsafe { + w + // Enable Continuous mode + .cont().bit($this.timer_mode == HrTimerMode::Continuous) + .retrig().bit($this.timer_mode == HrTimerMode::SingleShotRetriggerable) + + // TODO: add support for more modes + + // Interleaved mode + .intlvd().bits(intlvd) + + // half/double interleaved mode + .half().bit(half) + + // Set prescaler + .$ck_psc().bits(prescaler_bits as u8) + }); + + $( + tim.$timXcr2.modify(|_r, w| + // Set counting direction + w.udm().bit($this.counting_direction == HrCountingDirection::UpDown) + ); + + // Only available for timers with outputs(not HRTIM_MASTER) + let _ = tim.$outXr; + tim.$timXcr.modify(|_r, w| + // Push-Pull mode + w.pshpll().bit($this.enable_push_pull) + ); + )* + + // Write period + tim.$perXr.write(|w| unsafe { w.$perx().bits(period as u16) }); + + // Enable fault sources and lock configuration + $(unsafe { + // Enable fault sources + let fault_enable_bits = $this.fault_enable_bits as u32; + tim.$fltXr.write(|w| w + .flt1en().bit(fault_enable_bits & (1 << 0) != 0) + .flt2en().bit(fault_enable_bits & (1 << 1) != 0) + .flt3en().bit(fault_enable_bits & (1 << 2) != 0) + .flt4en().bit(fault_enable_bits & (1 << 3) != 0) + .flt5en().bit(fault_enable_bits & (1 << 4) != 0) + .flt6en().bit(fault_enable_bits & (1 << 5) != 0) + ); + + // ... and lock configuration + tim.$fltXr.modify(|_r, w| w.fltlck().set_bit()); + + tim.$outXr.modify(|_r, w| w + // Set actions on fault for both outputs + .fault1().bits($this.fault1_bits) + .fault2().bits($this.fault2_bits) + + // Set output polarity for both outputs + .pol1().bit($this.out1_polarity == Polarity::ActiveLow) + .pol2().bit($this.out2_polarity == Polarity::ActiveLow) + ); + if let Some(deadtime) = $this.deadtime { + let DeadtimeConfig { + prescaler, + deadtime_rising_value, + deadtime_rising_sign, + deadtime_falling_value, + deadtime_falling_sign, + } = deadtime; + + // SAFETY: DeadtimeConfig makes sure rising and falling values are valid + // and DeadtimePrescaler has its own garantuee + tim.$dtXr.modify(|_r, w| w + .dtprsc().bits(prescaler as u8) + .dtrx().bits(deadtime_rising_value) + .sdtrx().bit(deadtime_rising_sign) + .dtfx().bits(deadtime_falling_value) + .sdtfx().bit(deadtime_falling_sign) + + // Lock configuration + .dtflkx().set_bit() + .dtfslkx().set_bit() + .dtrlkx().set_bit() + .dtrslkx().set_bit() + ); + tim.$outXr.modify(|_r, w| w.dten().set_bit()); + } + + // External event configs + let eev_cfg = $this.eev_cfg.clone(); + tim.$eefXr1.write(|w| w + .ee1ltch().bit(eev_cfg.eev1.latch_bit).ee1fltr().bits(eev_cfg.eev1.filter_bits) + .ee2ltch().bit(eev_cfg.eev2.latch_bit).ee2fltr().bits(eev_cfg.eev2.filter_bits) + .ee3ltch().bit(eev_cfg.eev3.latch_bit).ee3fltr().bits(eev_cfg.eev3.filter_bits) + .ee4ltch().bit(eev_cfg.eev4.latch_bit).ee4fltr().bits(eev_cfg.eev4.filter_bits) + .ee5ltch().bit(eev_cfg.eev5.latch_bit).ee5fltr().bits(eev_cfg.eev5.filter_bits) + ); + tim.$eefXr2.write(|w| w + .ee6ltch().bit(eev_cfg.eev6.latch_bit).ee6fltr().bits(eev_cfg.eev6.filter_bits) + .ee7ltch().bit(eev_cfg.eev7.latch_bit).ee7fltr().bits(eev_cfg.eev7.filter_bits) + .ee8ltch().bit(eev_cfg.eev8.latch_bit).ee8fltr().bits(eev_cfg.eev8.filter_bits) + .ee9ltch().bit(eev_cfg.eev9.latch_bit).ee9fltr().bits(eev_cfg.eev9.filter_bits) + .ee10ltch().bit(eev_cfg.eev10.latch_bit).ee10fltr().bits(eev_cfg.eev10.filter_bits) + ); + tim.$Xeefr3.write(|w| w + .eevace().bit(eev_cfg.event_counter_enable_bit) + // External Event A Counter Reset"] + //.eevacres().bit() + .eevarstm().bit(eev_cfg.event_counter_reset_mode_bit) + .eevasel().bits(eev_cfg.event_counter_source_bits) + .eevacnt().bits(eev_cfg.event_counter_threshold_bits) + ); + })* + + + hrtim_finalize_body!($PreloadSource, $this, tim, $timXcr); + + // Set repetition counter + unsafe { tim.$rep.write(|w| w.$repx().bits($this.repetition_counter)); } + + // Enable interrupts + tim.$dier.modify(|_r, w| w.$repie().bit($this.enable_repetition_interrupt)); + + // Start timer + //let master = unsafe { &*HRTIM_MASTER::ptr() }; + //master.mcr.modify(|_r, w| { w.$tXcen().set_bit() }); + + unsafe { + MaybeUninit::uninit().assume_init() + } + }}; + + (PreloadSource, $this:expr, $tim:expr, $timXcr:ident) => {{ + match $this.preload_source { + Some(PreloadSource::OnCounterReset) => { + $tim.$timXcr.modify(|_r, w| w + .tx_rstu().set_bit() + .preen().set_bit() + ) + }, + Some(PreloadSource::OnMasterTimerUpdate) => { + $tim.$timXcr.modify(|_r, w| w + .mstu().set_bit() + .preen().set_bit() + ) + } + Some(PreloadSource::OnRepetitionUpdate) => { + $tim.$timXcr.modify(|_r, w| w + .tx_repu().set_bit() + .preen().set_bit() + ) + } + None => () + } + }}; + + (MasterPreloadSource, $this:expr, $tim:expr, $timXcr:ident) => {{ + match $this.preload_source { + Some(MasterPreloadSource::OnMasterRepetitionUpdate) => { + $tim.$timXcr.modify(|_r, w| w + .mrepu().set_bit() + .preen().set_bit() + ) + } + None => () + } + }}; +} + +macro_rules! hrtim_common_methods { + ($TIMX:ident, $PS:ident) => { + /// Set the PWM frequency; will overwrite the previous prescaler and period + /// The requested frequency will be rounded to the nearest achievable frequency; the actual frequency may be higher or lower than requested. + pub fn frequency>(mut self, freq: T) -> Self { + self.count = CountSettings::Frequency(freq.into()); + + self + } + + /// Set the prescaler; PWM count runs at base_frequency/(prescaler+1) + pub fn prescaler

( + self, + _prescaler: P, + ) -> HrPwmBuilder<$TIMX, P, $PS, ::Out

> + where + P: HrtimPrescaler, + { + let HrPwmBuilder { + _tim, + _prescaler: _, + _out, + timer_mode, + fault_enable_bits, + fault1_bits, + fault2_bits, + enable_push_pull, + interleaved_mode, + counting_direction, + base_freq, + count, + preload_source, + repetition_counter, + deadtime, + enable_repetition_interrupt, + eev_cfg, + out1_polarity, + out2_polarity, + } = self; + + let period = match count { + CountSettings::Frequency(_) => u16::MAX, + CountSettings::Period(period) => period, + }; + + let count = CountSettings::Period(period); + + HrPwmBuilder { + _tim, + _prescaler: PhantomData, + _out: PhantomData, + timer_mode, + fault_enable_bits, + fault1_bits, + fault2_bits, + enable_push_pull, + interleaved_mode, + counting_direction, + base_freq, + count, + preload_source, + repetition_counter, + deadtime, + enable_repetition_interrupt, + eev_cfg, + out1_polarity, + out2_polarity, + } + } + + pub fn timer_mode(mut self, timer_mode: HrTimerMode) -> Self { + self.timer_mode = timer_mode; + self + } + + // TODO: Allow setting multiple? + pub fn preload(mut self, preload_source: $PS) -> Self { + self.preload_source = Some(preload_source); + self + } + + /// Set the period; PWM count runs from 0 to period, repeating every (period+1) counts + pub fn period(mut self, period: u16) -> Self { + self.count = CountSettings::Period(period); + self + } + + /// Set repetition counter, useful to reduce interrupts generated + /// from timer by a factor (repetition_counter + 1) + pub fn repetition_counter(mut self, repetition_counter: u8) -> Self { + self.repetition_counter = repetition_counter; + self + } + + pub fn enable_repetition_interrupt(mut self) -> Self { + self.enable_repetition_interrupt = true; + self + } + + pub fn eev_cfg(mut self, eev_cfg: EevCfgs<$TIMX>) -> Self { + self.eev_cfg = eev_cfg; + self + } + }; +} + +// Implement PWM configuration for timer +macro_rules! hrtim_hal { + ($($TIMX:ident: ($timXcr:ident, $timXcr2:ident, $perXr:ident, $tXcen:ident, $rep:ident, $repx:ident, $dier:ident, $repie:ident, + $fltXr:ident, $eefXr1:ident, $eefXr2:ident, $Xeefr3:ident, $outXr:ident, $dtXr:ident),)+) => { + $( + + // Implement HrPwmExt trait for hrtimer + impl HrPwmExt for $TIMX { + fn pwm( + self, + pins: PINS, + frequency: T, + control: &mut HrPwmControl, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins + ToHrOut, + T: Into, + U: HrtimChannel, + { + let _= self.pwm_advanced(pins, rcc).frequency(frequency).finalize(control); + + unsafe { MaybeUninit::::uninit().assume_init() } + } + } + + impl HrPwmAdvExt for $TIMX { + type PreloadSource = PreloadSource; + + fn pwm_advanced( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder> + where + PINS: Pins + ToHrOut, + CHANNEL: HrtimChannel + { + // TODO: That 32x factor... Is that included below, or should we + // do that? Also that will likely risk overflowing u32 since + // 170MHz * 32 = 5.44GHz > u32::MAX.Hz() + let clk = HertzU64::from(HRTIM_COMMON::get_timer_frequency(&rcc.clocks)) * 32; + + HrPwmBuilder { + _tim: PhantomData, + _prescaler: PhantomData, + _out: PhantomData, + timer_mode: HrTimerMode::Continuous, + fault_enable_bits: 0b000000, + fault1_bits: 0b00, + fault2_bits: 0b00, + counting_direction: HrCountingDirection::Up, + base_freq: clk, + count: CountSettings::Period(u16::MAX), + preload_source: None, + enable_push_pull: false, + interleaved_mode: InterleavedMode::Disabled, + repetition_counter: 0, + deadtime: None, + enable_repetition_interrupt: false, + eev_cfg: EevCfgs::default(), + out1_polarity: Polarity::ActiveHigh, + out2_polarity: Polarity::ActiveHigh, + } + } + } + + impl + HrPwmBuilder<$TIMX, PSCL, PreloadSource, OUT> + where + PSCL: HrtimPrescaler, + OUT: ToHrOut, + { + pub fn finalize(self, _control: &mut HrPwmControl) -> ( + HrTim<$TIMX, PSCL, + HrCapt<$TIMX, PSCL, capture::Ch1, capture::NoDma>, + HrCapt<$TIMX, PSCL, capture::Ch2, capture::NoDma> + >, ( + HrCr1<$TIMX, PSCL>, + HrCr2<$TIMX, PSCL>, + HrCr3<$TIMX, PSCL>, + HrCr4<$TIMX, PSCL> + ), + OUT, + timer::DmaChannel<$TIMX>, + ) { + + hrtim_finalize_body!( + self, PreloadSource, + $TIMX: ($timXcr, ck_pscx, $perXr, perx, $tXcen, $rep, $repx, $dier, $repie, $timXcr2, $fltXr, $eefXr1, $eefXr2, $Xeefr3, $outXr, $dtXr), + ) + } + + hrtim_common_methods!($TIMX, PreloadSource); + + pub fn with_fault_source(mut self, _fault_source: FS) -> Self + where FS: FaultSource + { + self.fault_enable_bits |= FS::ENABLE_BITS; + + self + } + + pub fn fault_action1(mut self, fault_action1: FaultAction) -> Self { + self.fault1_bits = fault_action1 as _; + + self + } + + pub fn fault_action2(mut self, fault_action2: FaultAction) -> Self { + self.fault2_bits = fault_action2 as _; + + self + } + + pub fn out1_polarity(mut self, polarity: Polarity) -> Self { + self.out1_polarity = polarity; + + self + } + + pub fn out2_polarity(mut self, polarity: Polarity) -> Self { + self.out2_polarity = polarity; + + self + } + + /// Enable or disable Push-Pull mode + /// + /// Enabling Push-Pull mode will make output 1 and 2 + /// alternate every period with one being + /// inactive and the other getting to output its wave form + /// as normal + /// + /// ---- . ---- + ///out1 | | . | | + /// | | . | | + /// -------- ---------------------------- -------------------- + /// . ------ . ------ + ///out2 . | | . | | + /// . | | . | | + /// ------------------------ ---------------------------- -- + /// + /// NOTE: setting this will overide any 'Swap Mode' set + pub fn push_pull_mode(mut self, enable: bool) -> Self { + // TODO: add check for incompatible modes + self.enable_push_pull = enable; + + self + } + + /// Set counting direction + /// + /// See [`HrCountingDirection`] + pub fn counting_direction(mut self, counting_direction: HrCountingDirection) -> Self { + self.counting_direction = counting_direction; + + self + } + + /// Set interleaved or half modes + /// + /// NOTE: Check [`InterleavedMode`] for more info about special cases + pub fn interleaved_mode(mut self, mode: InterleavedMode) -> Self { + self.interleaved_mode = mode; + + self + } + + pub fn deadtime(mut self, deadtime: DeadtimeConfig) -> Self { + self.deadtime = Some(deadtime); + + self + } + + //pub fn swap_mode(mut self, enable: bool) -> Self + } + )+ + }; +} + +macro_rules! hrtim_hal_master { + ($($TIMX:ident: ($timXcr:ident, $ck_psc:ident, $perXr:ident, $perx:ident, $rep:ident, $tXcen:ident, $dier:ident, $repie:ident),)+) => {$( + impl HrPwmAdvExt for $TIMX { + type PreloadSource = MasterPreloadSource; + + fn pwm_advanced( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder> + where + PINS: Pins + ToHrOut, // TODO: figure out + CHANNEL: HrtimChannel + { + // TODO: That 32x factor... Is that included below, or should we + // do that? Also that will likely risk overflowing u32 since + // 170MHz * 32 = 5.44GHz > u32::MAX.Hz() + let clk = HertzU64::from(HRTIM_COMMON::get_timer_frequency(&rcc.clocks)) * 32; + + HrPwmBuilder { + _tim: PhantomData, + _prescaler: PhantomData, + _out: PhantomData, + timer_mode: HrTimerMode::Continuous, + fault_enable_bits: 0b000000, + fault1_bits: 0b00, + fault2_bits: 0b00, + counting_direction: HrCountingDirection::Up, + base_freq: clk, + count: CountSettings::Period(u16::MAX), + preload_source: None, + enable_push_pull: false, + interleaved_mode: InterleavedMode::Disabled, + repetition_counter: 0, + deadtime: None, + enable_repetition_interrupt: false, + eev_cfg: EevCfgs::default(), + out1_polarity: Polarity::ActiveHigh, + out2_polarity: Polarity::ActiveHigh, + } + } + } + + impl + HrPwmBuilder<$TIMX, PSCL, MasterPreloadSource, OUT> + where + PSCL: HrtimPrescaler, + OUT: ToHrOut, + { + pub fn finalize(self, _control: &mut HrPwmControl) -> ( + HrTim<$TIMX, PSCL, + HrCapt<$TIMX, PSCL, capture::Ch1, capture::NoDma>, + HrCapt<$TIMX, PSCL, capture::Ch2, capture::NoDma>, + >, ( + HrCr1<$TIMX, PSCL>, + HrCr2<$TIMX, PSCL>, + HrCr3<$TIMX, PSCL>, + HrCr4<$TIMX, PSCL> + ), + timer::DmaChannel<$TIMX>, + ) { + + hrtim_finalize_body!(self, MasterPreloadSource, $TIMX: ($timXcr, $ck_psc, $perXr, $perx, $tXcen, $rep, $rep, $dier, $repie),) + } + + hrtim_common_methods!($TIMX, MasterPreloadSource); + } + )*} +} + +macro_rules! hrtim_pin_hal { + ($($TIMX:ident: + ($CH:ident, $perXr:ident, $cmpXYr:ident, $cmpYx:ident, $cmpY:ident, $tXYoen:ident, $tXYodis:ident),)+ + ) => { + $( + impl hal::PwmPin for Pwm<$TIMX, $CH, COMP, POL, NPOL> + where Pwm<$TIMX, $CH, COMP, POL, NPOL>: PwmPinEnable { + type Duty = u16; + + // You may not access self in the following methods! + // See unsafe above + + fn disable(&mut self) { + self.ccer_disable(); + } + + fn enable(&mut self) { + self.ccer_enable(); + } + + fn get_duty(&self) -> Self::Duty { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.read().$cmpYx().bits() + } + + fn get_max_duty(&self) -> Self::Duty { + let tim = unsafe { &*$TIMX::ptr() }; + + let arr = tim.$perXr.read().perx().bits(); + + // One PWM cycle is ARR+1 counts long + // Valid PWM duty cycles are 0 to ARR+1 + // However, if ARR is 65535 on a 16-bit timer, we can't add 1 + // In that case, 100% duty cycle is not possible, only 65535/65536 + if arr == Self::Duty::MAX { + arr + } + else { + arr + 1 + } + } + + /// Set duty cycle + /// + /// NOTE: Please observe limits(RM0440 "Period and compare registers min and max values"): + /// | Prescaler | Min duty | Max duty | + /// |-----------|----------|----------| + /// | 1 | 0x0060 | 0xFFDF | + /// | 2 | 0x0030 | 0xFFEF | + /// | 4 | 0x0018 | 0xFFF7 | + /// | 8 | 0x000C | 0xFFFB | + /// | 16 | 0x0006 | 0xFFFD | + /// | 32 | 0x0003 | 0xFFFD | + /// | 64 | 0x0003 | 0xFFFD | + /// | 128 | 0x0003 | 0xFFFD | + /// + /// Also, writing 0 as duty is only valid for CR1 and CR3 during a set of + /// specific conditions(see RM0440 "Null duty cycle exception case"): + /// – the output SET event is generated by the PERIOD event + /// – the output RESET if generated by the compare 1 (respectively compare 3) event + /// – the compare 1 (compare 3) event is active within the timer unit itself, and not used + /// for other timing units + fn set_duty(&mut self, duty: Self::Duty) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.write(|w| unsafe { w.$cmpYx().bits(duty) }); + } + } + + // Enable implementation for ComplementaryImpossible + impl PwmPinEnable for Pwm<$TIMX, $CH, ComplementaryImpossible, POL, NPOL> { + fn ccer_enable(&mut self) { + // TODO: Should this part only be in Pwm::enable? + // Enable output Y on channel X + // This is a set-only register, no risk for data race + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.oenr.write(|w| { w.$tXYoen().set_bit() }); + } + fn ccer_disable(&mut self) { + // TODO: Should this part only be in Pwm::disable + // Disable output Y on channel X + // This is a write only register, no risk for data race + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.odisr.write(|w| { w.$tXYodis().set_bit() }); + } + } + )+ + } +} + +hrtim_hal! { + HRTIM_TIMA: (timacr, timacr2, perar, tacen, repar, repx, timadier, repie, fltar, eefar1, eefar2, aeefr3, outar, dtar), + HRTIM_TIMB: (timbcr, timbcr2, perbr, tbcen, repbr, repx, timbdier, repie, fltbr, eefbr1, eefbr2, beefr3, outbr, dtbr), + HRTIM_TIMC: (timccr, timccr2, percr, tccen, repcr, repx, timcdier, repie, fltcr, eefcr1, eefcr2, ceefr3, outcr, dtcr), + HRTIM_TIMD: (timdcr, timdcr2, perdr, tdcen, repdr, repx, timddier, repie, fltdr, eefdr1, eefdr2, deefr3, outdr, dtdr), + HRTIM_TIME: (timecr, timecr2, perer, tecen, reper, repx, timedier, repie, flter, eefer1, eefer2, eeefr3, outer, dter), + HRTIM_TIMF: (timfcr, timfcr2, perfr, tfcen, repfr, repx, timfdier, repie, fltfr, eeffr1, eeffr2, feefr3, outfr, dtfr), +} + +hrtim_hal_master! { + HRTIM_MASTER: (mcr, ck_psc, mper, mper, mrep, mcen, mdier, mrepie), +} + +hrtim_pin_hal! { + HRTIM_TIMA: (CH1, perar, cmp1ar, cmp1x, cmp1, ta1oen, ta1odis), + HRTIM_TIMA: (CH2, perar, cmp3ar, cmp3x, cmp3, ta2oen, ta2odis), + + HRTIM_TIMB: (CH1, perbr, cmp1br, cmp1x, cmp1, tb1oen, tb1odis), + HRTIM_TIMB: (CH2, perbr, cmp3br, cmp3x, cmp3, tb2oen, tb2odis), + + HRTIM_TIMC: (CH1, percr, cmp1cr, cmp1x, cmp1, tc1oen, tc1odis), + HRTIM_TIMC: (CH2, percr, cmp3cr, cmp3x, cmp3, tc2oen, tc2odis), + + HRTIM_TIMD: (CH1, perdr, cmp1dr, cmp1x, cmp1, td1oen, td1odis), + HRTIM_TIMD: (CH2, perdr, cmp3dr, cmp3x, cmp3, td2oen, td2odis), + + HRTIM_TIME: (CH1, perer, cmp1er, cmp1x, cmp1, te1oen, te1odis), + HRTIM_TIME: (CH2, perer, cmp3er, cmp3x, cmp3, te2oen, te2odis), + + HRTIM_TIMF: (CH1, perfr, cmp1fr, cmp1x, cmp1, tf1oen, tf1odis), + HRTIM_TIMF: (CH2, perfr, cmp3fr, cmp3x, cmp3, tf2oen, tf2odis), +} + +/// # Safety +/// Only implement for valid prescalers with correct values +pub unsafe trait HrtimPrescaler: Default { + const BITS: u8; + const VALUE: u8; + + /// Minimum allowed value for compare registers used with the timer with this prescaler + /// + /// NOTE: That for CR1 and CR3, 0 is also allowed + const MIN_CR: u16; + + /// Maximum allowed value for compare registers used with the timer with this prescaler + const MAX_CR: u16; +} + +macro_rules! impl_pscl { + ($($t:ident => $b:literal, $v:literal, $min:literal, $max:literal)+) => {$( + #[derive(Copy, Clone, Default)] + pub struct $t; + unsafe impl HrtimPrescaler for $t { + const BITS: u8 = $b; + const VALUE: u8 = $v; + const MIN_CR: u16 = $min; + const MAX_CR: u16 = $max; + } + )+}; +} + +impl_pscl! { + Pscl1 => 0b000, 1, 0x0060, 0xFFDF + Pscl2 => 0b001, 2, 0x0030, 0xFFEF + Pscl4 => 0b010, 4, 0x0018, 0xFFF7 + Pscl8 => 0b011, 8, 0x000C, 0xFFFB + Pscl16 => 0b100, 16, 0x0006, 0xFFFD + Pscl32 => 0b101, 32, 0x0003, 0xFFFD + Pscl64 => 0b110, 64, 0x0003, 0xFFFD + Pscl128 => 0b111, 128, 0x0003, 0xFFFD +} + +/// HrTim timer +struct TimerHrTim(PhantomData); + +impl pwm::TimerType for TimerHrTim { + // Period calculator for 16-bit hrtimers + // + // NOTE: This function will panic if the calculated period can not fit into 16 bits + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16) { + let ideal_period = pwm::Timer32Bit::calculate_frequency(base_freq, freq, alignment).0 + 1; + + let prescale = u32::from(PSC::VALUE); + + // Round to the nearest period + let period = (ideal_period + (prescale >> 1)) / prescale - 1; + + // It IS possible to fail this assert + assert!(period <= 0xFFFF); + + (period, PSC::BITS.into()) + } +} diff --git a/src/hrtim/output.rs b/src/hrtim/output.rs new file mode 100644 index 00000000..b74d6d72 --- /dev/null +++ b/src/hrtim/output.rs @@ -0,0 +1,218 @@ +use crate::stm32::{HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF}; +use core::marker::PhantomData; + +use super::event::EventSource; +use crate::{ + gpio::{ + gpioa::{PA10, PA11, PA8, PA9}, + gpiob::{PB12, PB13, PB14, PB15}, + gpioc::{PC6, PC7, PC8, PC9}, + Alternate, AF13, AF3, + }, + pwm::{ActiveHigh, ComplementaryImpossible, Pins, Pwm}, + stm32::HRTIM_COMMON, +}; + +macro_rules! hrtim_out { + ($($TIMX:ident: $out_type:ident: $tXYoen:ident, $tXYodis:ident, $tXYods:ident, $setXYr:ident, $rstXYr:ident,)+) => {$( + impl HrOutput<$TIMX, PSCL> for $out_type<$TIMX, PSCL> { + fn enable(&mut self) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.oenr.write(|w| { w.$tXYoen().set_bit() }); + } + + fn disable(&mut self) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.odisr.write(|w| { w.$tXYodis().set_bit() }); + } + + fn enable_set_event>(&mut self, _set_event: &ES) { + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.$setXYr.modify(|r, w| w.bits(r.bits() | ES::BITS)); } + } + fn disable_set_event>(&mut self, _set_event: &ES) { + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.$setXYr.modify(|r, w| w.bits(r.bits() & !ES::BITS)); } + } + + fn enable_rst_event>(&mut self, _reset_event: &ES) { + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.$rstXYr.modify(|r, w| w.bits(r.bits() | ES::BITS)); } + } + fn disable_rst_event>(&mut self, _reset_event: &ES) { + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.$rstXYr.modify(|r, w| w.bits(r.bits() & !ES::BITS)); } + } + + fn get_state(&self) -> State { + let ods; + let oen; + + unsafe { + let common = &*HRTIM_COMMON::ptr(); + ods = common.odsr.read().$tXYods().bit_is_set(); + oen = common.oenr.read().$tXYoen().bit_is_set(); + } + + match (oen, ods) { + (true, _) => State::Running, + (false, false) => State::Idle, + (false, true) => State::Fault + } + } + } + )+}; +} + +hrtim_out! { + HRTIM_TIMA: HrOut1: ta1oen, ta1odis, ta1ods, seta1r, rsta1r, + HRTIM_TIMA: HrOut2: ta2oen, ta2odis, ta2ods, seta2r, rsta2r, + + HRTIM_TIMB: HrOut1: tb1oen, tb1odis, tb1ods, setb1r, rstb1r, + HRTIM_TIMB: HrOut2: tb2oen, tb2odis, tb2ods, setb2r, rstb2r, + + HRTIM_TIMC: HrOut1: tc1oen, tc1odis, tc1ods, setc1r, rstc1r, + HRTIM_TIMC: HrOut2: tc2oen, tc2odis, tc2ods, setc2r, rstc2r, + + HRTIM_TIMD: HrOut1: td1oen, td1odis, td1ods, setd1r, rstd1r, + HRTIM_TIMD: HrOut2: td2oen, td2odis, td2ods, setd2r, rstd2r, + + HRTIM_TIME: HrOut1: te1oen, te1odis, te1ods, sete1r, rste1r, + HRTIM_TIME: HrOut2: te2oen, te2odis, te2ods, sete2r, rste2r, + + HRTIM_TIMF: HrOut1: tf1oen, tf1odis, tf1ods, setf1r, rstf1r, + HRTIM_TIMF: HrOut2: tf2oen, tf2odis, tf2ods, setf2r, rstf2r, +} + +pub trait HrOutput { + /// Enable this output + fn enable(&mut self); + + /// Disable this output + fn disable(&mut self); + + /// Set this output to active every time the specified event occurs + /// + /// NOTE: Enabling the same event for both SET and RESET + /// will make that event TOGGLE the output + fn enable_set_event>(&mut self, set_event: &ES); + + /// Stop listening to the specified event + fn disable_set_event>(&mut self, set_event: &ES); + + /// Set this output to *not* active every time the specified event occurs + /// + /// NOTE: Enabling the same event for both SET and RESET + /// will make that event TOGGLE the output + fn enable_rst_event>(&mut self, reset_event: &ES); + + /// Stop listening to the specified event + fn disable_rst_event>(&mut self, reset_event: &ES); + + /// Get current state of the output + fn get_state(&self) -> State; +} + +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum State { + Idle, + Running, + Fault, +} + +pub unsafe trait ToHrOut { + type Out: ToHrOut; +} + +unsafe impl ToHrOut for (PA, PB) +where + PA: ToHrOut, + PB: ToHrOut, +{ + type Out = (PA::Out, PB::Out); +} + +pub struct HrOut1(PhantomData<(TIM, PSCL)>); +pub struct HrOut2(PhantomData<(TIM, PSCL)>); + +macro_rules! pins { + ($($TIMX:ty: CH1: $CH1:ty, CH2: $CH2:ty)+) => { + $( + impl Pins<$TIMX, CH1, ComplementaryImpossible> for $CH1 { + type Channel = Pwm<$TIMX, CH1, ComplementaryImpossible, ActiveHigh, ActiveHigh>; + } + + impl Pins<$TIMX, CH2, ComplementaryImpossible> for $CH2 { + type Channel = Pwm<$TIMX, CH2, ComplementaryImpossible, ActiveHigh, ActiveHigh>; + } + + unsafe impl ToHrOut for $CH1 { + type Out = HrOut1<$TIMX, PSCL>; + } + + unsafe impl ToHrOut for $CH2 { + type Out = HrOut2<$TIMX, PSCL>; + } + + unsafe impl ToHrOut for HrOut1<$TIMX, PSCL> { + type Out

= HrOut1<$TIMX, P>; + } + + unsafe impl ToHrOut for HrOut2<$TIMX, PSCL> { + type Out

= HrOut2<$TIMX, P>; + } + )+ + } +} + +pins! { + HRTIM_TIMA: CH1: PA8>, CH2: PA9> + + HRTIM_TIMB: CH1: PA10>, CH2: PA11> + HRTIM_TIMC: CH1: PB12>, CH2: PB13> + HRTIM_TIMD: CH1: PB14>, CH2: PB15> + + HRTIM_TIME: CH1: PC8>, CH2: PC9> + HRTIM_TIMF: CH1: PC6>, CH2: PC7> +} + +impl Pins for () { + type Channel = (); +} + +unsafe impl ToHrOut for () { + type Out = (); +} + +// automatically implement Pins trait for tuples of individual pins +macro_rules! pins_tuples { + // Tuple of two pins + ($(($CHA:ident, $CHB:ident)),*) => { + $( + impl Pins, $CHB), (TA, TB)> for (CHA, CHB) + where + CHA: Pins, TA>, + CHB: Pins, TB>, + { + type Channel = (Pwm, TA, ActiveHigh, ActiveHigh>, Pwm, TB, ActiveHigh, ActiveHigh>); + } + + impl HrtimChannel for ($CHA, $CHB) {} + )* + }; +} + +pins_tuples! { + (CH1, CH2), + (CH2, CH1) +} + +pub struct CH1(PhantomData); +pub struct CH2(PhantomData); + +impl HrtimChannel for () {} +pub trait HrtimChannel {} + +impl HrtimChannel for CH1 {} +impl HrtimChannel for CH2 {} diff --git a/src/hrtim/timer.rs b/src/hrtim/timer.rs new file mode 100644 index 00000000..9eee991d --- /dev/null +++ b/src/hrtim/timer.rs @@ -0,0 +1,316 @@ +use crate::stm32::{ + HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF, +}; +use core::marker::PhantomData; + +use super::{ + capture::{self, HrCapt}, + control::HrPwmControl, + HrtimPrescaler, +}; + +pub struct HrTim { + _timer: PhantomData, + _prescaler: PhantomData, + capture_ch1: CPT1, + capture_ch2: CPT2, +} + +/// This is the DMA channel of a HRTIM timer +/// +/// Every HRTIM timer including the master timer has a DMA channel +pub struct DmaChannel { + _x: PhantomData, +} + +pub trait HrTimer { + type Timer; + type Prescaler: HrtimPrescaler; + + /// Get period of timer in number of ticks + /// + /// This is also the maximum duty usable for `HrCompareRegister::set_duty` + fn get_period(&self) -> u16; + + /// Set period of timer in number of ticks + /// + /// NOTE: This will affect the maximum duty usable for `HrCompareRegister::set_duty` + fn set_period(&mut self, period: u16); + + /// Start timer + fn start(&mut self, _hr_control: &mut HrPwmControl); + + /// Stop timer + fn stop(&mut self, _hr_control: &mut HrPwmControl); + + /// Stop timer and reset counter + fn stop_and_reset(&mut self, _hr_control: &mut HrPwmControl); + + fn clear_repetition_interrupt(&mut self); + + /// Make a handle to this timers reset event to use as adc trigger + fn as_reset_adc_trigger(&self) -> super::adc_trigger::TimerReset; + + /// Make a handle to this timers period event to use as adc trigger + fn as_period_adc_trigger(&self) -> super::adc_trigger::TimerPeriod; +} + +pub trait HrSlaveTimer: HrTimer { + type CaptureCh1; + type CaptureCh2; + + /// Start listening to the specified event + fn enable_reset_event>( + &mut self, + _event: &E, + ); + + /// Stop listening to the specified event + fn disable_reset_event>( + &mut self, + _event: &E, + ); +} + +/// Trait for unsplit slave timer which still contains its capture modules +pub trait HrSlaveTimerCpt: HrSlaveTimer { + fn capture_ch1(&mut self) -> &mut ::CaptureCh1; + fn capture_ch2(&mut self) -> &mut ::CaptureCh2; + fn split_capture( + self, + ) -> ( + HrTim, + HrCapt, + HrCapt, + ); +} + +macro_rules! hrtim_timer { + ($( + $TIMX:ident: + $cntXr:ident, + $cntx:ident, + $perXr:ident, + $tXcen:ident, + $perx:ident, + $rep:ident, + $repx:ident, + $dier:ident, + $repie:ident, + $icr:ident, + $repc:ident, + $(($rstXr:ident))*, + )+) => {$( + impl HrTimer for HrTim<$TIMX, PSCL, CPT1, CPT2> { + type Prescaler = PSCL; + type Timer = $TIMX; + + fn get_period(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$perXr.read().$perx().bits() + } + fn set_period(&mut self, period: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$perXr.write(|w| unsafe { w.$perx().bits(period as u16) }); + } + + /// Start timer + fn start(&mut self, _hr_control: &mut HrPwmControl) { + // Start timer + + // SAFETY: Since we hold _hr_control there is no risk for a race condition + let master = unsafe { &*HRTIM_MASTER::ptr() }; + master.mcr.modify(|_r, w| { w.$tXcen().set_bit() }); + } + + /// Stop timer + fn stop(&mut self, _hr_control: &mut HrPwmControl) { + // Stop counter + // SAFETY: Since we hold _hr_control there is no risk for a race condition + let master = unsafe { &*HRTIM_MASTER::ptr() }; + master.mcr.modify(|_r, w| { w.$tXcen().set_bit() }); + } + + /// Stop timer and reset counter + fn stop_and_reset(&mut self, _hr_control: &mut HrPwmControl) { + self.stop(_hr_control); + + // Reset counter + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.$cntXr.write(|w| w.$cntx().bits(0)); } + } + + /// Make a handle to this timers reset event to use as adc trigger + fn as_reset_adc_trigger(&self) -> super::adc_trigger::TimerReset { + super::adc_trigger::TimerReset(PhantomData) + } + + /// Make a handle to this timers period event to use as adc trigger + fn as_period_adc_trigger(&self) -> super::adc_trigger::TimerPeriod { + super::adc_trigger::TimerPeriod(PhantomData) + } + + fn clear_repetition_interrupt(&mut self) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$icr.write(|w| w.$repc().set_bit()); + } + } + + impl HrTim<$TIMX, PSCL, CPT1, CPT2> { + pub fn set_repetition_counter(&mut self, repetition_counter: u8) { + let tim = unsafe { &*$TIMX::ptr() }; + + unsafe { tim.$rep.write(|w| w.$repx().bits(repetition_counter)); } + } + + pub fn enable_repetition_interrupt(&mut self, enable: bool) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$dier.modify(|_r, w| w.$repie().bit(enable)); + } + } + + $( + impl HrSlaveTimer for HrTim<$TIMX, PSCL, CPT1, CPT2> { + type CaptureCh1 = HrCapt; + type CaptureCh2 = HrCapt; + + /// Reset this timer every time the specified event occurs + /// + /// Behaviour depends on `timer_mode`: + /// + /// * `HrTimerMode::SingleShotNonRetriggable`: Enabling the timer enables it but does not start it. + /// A first reset event starts the counting and any subsequent reset is ignored until the counter + /// reaches the PER value. The PER event is then generated and the counter is stopped. A reset event + /// restarts the counting from 0x0000. + /// * `HrTimerMode:SingleShotRetriggable`: Enabling the timer enables it but does not start it. + /// A reset event starts the counting if the counter is stopped, otherwise it clears the counter. + /// When the counter reaches the PER value, the PER event is generated and the counter is stopped. + /// A reset event restarts the counting from 0x0000. + /// * `HrTimerMode::Continuous`: Enabling the timer enables and starts it simultaneously. + /// When the counter reaches the PER value, it rolls-over to 0x0000 and resumes counting. + /// The counter can be reset at any time + fn enable_reset_event>(&mut self, _event: &E) { + let tim = unsafe { &*$TIMX::ptr() }; + + unsafe { tim.$rstXr.modify(|r, w| w.bits(r.bits() | E::BITS)); } + } + + /// Stop listening to the specified event + fn disable_reset_event>(&mut self, _event: &E) { + let tim = unsafe { &*$TIMX::ptr() }; + + unsafe { tim.$rstXr.modify(|r, w| w.bits(r.bits() & !E::BITS)); } + } + } + + impl HrSlaveTimerCpt for HrTim<$TIMX, PSCL, HrCapt<$TIMX, PSCL, capture::Ch1, capture::NoDma>, HrCapt<$TIMX, PSCL, capture::Ch2, capture::NoDma>> { + /// Access the timers first capture channel + fn capture_ch1(&mut self) -> &mut Self::CaptureCh1 { + &mut self.capture_ch1 + } + + /// Access the timers second capture channel + fn capture_ch2(&mut self) -> &mut Self::CaptureCh2 { + &mut self.capture_ch2 + } + + fn split_capture(self) -> (HrTim<$TIMX, PSCL, (), ()>, HrCapt<$TIMX, PSCL, capture::Ch1, capture::NoDma>, HrCapt<$TIMX, PSCL, capture::Ch2, capture::NoDma>) { + let HrTim{ + _timer, + _prescaler, + capture_ch1, + capture_ch2, + } = self; + + ( + HrTim{ + _timer, + _prescaler, + capture_ch1: (), + capture_ch2: (), + }, + capture_ch1, + capture_ch2, + ) + } + } + + /// Timer Period event + impl super::event::EventSource for HrTim<$TIMX, PSCL, CPT1, CPT2> { + // $rstXr + const BITS: u32 = 1 << 2; + } + + /// Timer Update event + /// + /// TODO: What dows this mean? + impl super::capture::CaptureEvent<$TIMX, PSCL> for HrTim<$TIMX, PSCL, CPT1, CPT2> { + const BITS: u32 = 1 << 1; + } + )* + )+} +} + +macro_rules! hrtim_timer_adc_trigger { + ($($TIMX:ident: + [$(($AdcTrigger:ident: [ + $((PER: $adc_trigger_bits_period:expr),)* + $((RST: $adc_trigger_bits_reset:expr)),* + ])),+] + ),+) => { + $($( + $(impl $AdcTrigger for super::adc_trigger::TimerReset> { + const BITS: u32 = $adc_trigger_bits_reset; + })* + + $(impl $AdcTrigger for super::adc_trigger::TimerPeriod> { + const BITS: u32 = $adc_trigger_bits_period; + })* + )*)* + } +} + +use super::adc_trigger::Adc13Trigger as Adc13; +use super::adc_trigger::Adc24Trigger as Adc24; +use super::adc_trigger::Adc579Trigger as Adc579; +use super::adc_trigger::Adc6810Trigger as Adc6810; + +hrtim_timer! { + HRTIM_MASTER: mcntr, mcnt, mper, mcen, mper, mrep, mrep, mdier, mrepie, micr, mrepc,, + + HRTIM_TIMA: cntar, cntx, perar, tacen, perx, repar, repx, timadier, repie, timaicr, repc, (rstar), + HRTIM_TIMB: cntr, cntx, perbr, tbcen, perx, repbr, repx, timbdier, repie, timbicr, repc, (rstbr), + HRTIM_TIMC: cntcr, cntx, percr, tccen, perx, repcr, repx, timcdier, repie, timcicr, repc, (rstcr), + HRTIM_TIMD: cntdr, cntx, perdr, tdcen, perx, repdr, repx, timddier, repie, timdicr, repc, (rstdr), + HRTIM_TIME: cnter, cntx, perer, tecen, perx, reper, repx, timedier, repie, timeicr, repc, (rster), + HRTIM_TIMF: cntfr, cntx, perfr, tfcen, perx, repfr, repx, timfdier, repie, timficr, repc, (rstfr), +} + +hrtim_timer_adc_trigger! { + HRTIM_MASTER: [(Adc13: [(PER: 1 << 4),]), (Adc24: [(PER: 1 << 4),]), (Adc579: [(PER: 4),]), (Adc6810: [(PER: 4),])], + + HRTIM_TIMA: [(Adc13: [(PER: 1 << 13), (RST: 1 << 14)]), (Adc24: [(PER: 1 << 13), ]), (Adc579: [(PER: 12), (RST: 13)]), (Adc6810: [(PER: 12), ])], + HRTIM_TIMB: [(Adc13: [(PER: 1 << 18), (RST: 1 << 19)]), (Adc24: [(PER: 1 << 17), ]), (Adc579: [(PER: 16), (RST: 17)]), (Adc6810: [(PER: 15), ])], + HRTIM_TIMC: [(Adc13: [(PER: 1 << 23), ]), (Adc24: [(PER: 1 << 21), (RST: 1 << 22)]), (Adc579: [(PER: 20), ]), (Adc6810: [(PER: 18), (RST: 19)])], + HRTIM_TIMD: [(Adc13: [(PER: 1 << 27), ]), (Adc24: [(PER: 1 << 26), (RST: 1 << 27)]), (Adc579: [(PER: 23), ]), (Adc6810: [(PER: 22), (RST: 23)])], + HRTIM_TIME: [(Adc13: [(PER: 1 << 31), ]), (Adc24: [ (RST: 1 << 31)]), (Adc579: [(PER: 26), ]), (Adc6810: [ ])], + HRTIM_TIMF: [(Adc13: [(PER: 1 << 24), (RST: 1 << 28)]), (Adc24: [(PER: 1 << 24), ]), (Adc579: [(PER: 30), (RST: 31)]), (Adc6810: [(PER: 31), ])] +} + +/// Master Timer Period event +impl super::event::TimerResetEventSource + for HrTim +{ + const BITS: u32 = 1 << 4; // MSTPER +} + +/// Master Timer Period event +impl super::event::EventSource + for HrTim +{ + const BITS: u32 = 1 << 7; // MSTPER +} diff --git a/src/hrtim/timer_eev_cfg.rs b/src/hrtim/timer_eev_cfg.rs new file mode 100644 index 00000000..96f79bde --- /dev/null +++ b/src/hrtim/timer_eev_cfg.rs @@ -0,0 +1,194 @@ +use core::marker::PhantomData; + +pub struct EevCfgs { + pub eev1: EevCfg, + pub eev2: EevCfg, + pub eev3: EevCfg, + pub eev4: EevCfg, + pub eev5: EevCfg, + pub eev6: EevCfg, + pub eev7: EevCfg, + pub eev8: EevCfg, + pub eev9: EevCfg, + pub eev10: EevCfg, + + // TODO: Expose these + // TODO: Note there are some peculiarities here with fast mode + // One way to prevent missuse would be to require a borrowed ExternalEventSource when setting + // filter/latching as well as the event_counter related settings below. + pub(crate) event_counter_enable_bit: bool, + pub(crate) event_counter_reset_mode_bit: bool, + pub(crate) event_counter_source_bits: u8, + pub(crate) event_counter_threshold_bits: u8, +} + +macro_rules! impl_setter { + ($eevX:ident) => { + pub fn $eevX(mut self, cfg: EevCfg) -> Self { + self.$eevX = cfg; + self + } + }; +} + +impl EevCfgs { + impl_setter!(eev1); + impl_setter!(eev2); + impl_setter!(eev3); + impl_setter!(eev4); + impl_setter!(eev5); + impl_setter!(eev6); + impl_setter!(eev7); + impl_setter!(eev8); + impl_setter!(eev9); + impl_setter!(eev10); +} + +impl Clone for EevCfgs { + fn clone(&self) -> Self { + Self { + eev1: self.eev1.clone(), + eev2: self.eev2.clone(), + eev3: self.eev3.clone(), + eev4: self.eev4.clone(), + eev5: self.eev5.clone(), + eev6: self.eev6.clone(), + eev7: self.eev7.clone(), + eev8: self.eev8.clone(), + eev9: self.eev9.clone(), + eev10: self.eev10.clone(), + event_counter_enable_bit: self.event_counter_enable_bit, + event_counter_reset_mode_bit: self.event_counter_reset_mode_bit, + event_counter_source_bits: self.event_counter_source_bits, + event_counter_threshold_bits: self.event_counter_threshold_bits, + } + } +} + +pub struct EevCfg { + _x: PhantomData, + pub(crate) filter_bits: u8, + pub(crate) latch_bit: bool, +} + +impl Clone for EevCfg { + fn clone(&self) -> Self { + Self { + _x: PhantomData, + filter_bits: self.filter_bits, + latch_bit: self.latch_bit, + } + } +} + +impl EevCfg { + /// NOTE: This can not be set if eev is in fast mode AND using `EevCfg::latching` + pub fn filter(mut self, filter: EventFilter) -> Self { + self.filter_bits = filter as u8; + self + } + + /// NOTE: This can not be set if eev is in fast mode AND using a `EevCfg::filter` + pub fn latching(mut self) -> Self { + self.latch_bit = true; + self + } +} + +/// Note: Whenever a compare register is used for filtering, the value must be strictly above 0. +pub enum EventFilter { + /// No filtering + None = 0b0000, + + /// Blanking from reset/rollover to Cmp1 + BlankingResetToCmp1 = 0b0001, + + /// This depends on counter mode: + /// * Up-counting mode: Blanking from reset/rollover to Cmp2 + /// * Up-down mode: Blanking from Cmp1 to Cmp2(only during up counting) + BlankingResetToCmp2OrCmp1ToCmp2InUdm = 0b0010, + + /// Blanking from reset/rollover to Cmp3 + BlankingResetToCmp3 = 0b0011, + + /// This depends on counter mode: + /// * Up-counting mode: Blanking from reset/rollover to Cmp4 + /// * Up-down mode: Blanking from Cmp3 to Cmp4(only during up counting) + BlankingResetToCmp4OrCmp3ToCmp4InUdm = 0b0100, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource1 = 0b0101, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource2 = 0b0110, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource3 = 0b0111, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource4 = 0b1000, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource5 = 0b1001, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource6 = 0b1010, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource7 = 0b1011, + + /// (RM 0440 table 226 'Filtering signals mapping per timer') + BlankingSource8 = 0b1100, + + /// This depends on counter mode: + /// * Up-counting mode: Windowing from reset/rollover to Cmp2 + /// * Up-down mode: Windowing from Cmp2 to Cmp3(only during up counting) + WindowingResetToCmp2OrCmp2ToCmp3InUdm = 0b1101, + + /// This depends on counter mode: + /// * Up-counting mode: Windowing from reset/rollover to Cmp3 + /// * Up-down mode: Windowing from Cmp2 to Cmp3(only during down counting) + WindowingResetToCmp3OrCmp2ToCmp3InUdm = 0b1110, + + /// This depends on counter mode: + /// * Up-counting mode: Windowing from reset/rollover to other timer `TIMWIN`'s Cmp2 event + /// * Up-down mode: Windowing from other timer `TIMWIN`'s Cmp2 during up counting to Cmp3 during down counting + /// + /// `TIMWIN` (RM 0440 table 227 'Windowing signals mapping per timer'): + /// + /// | Destination |`TIMA`|`TIMB`|`TIMC`|`TIMD`|`TIME`|`TIMF`| + /// |-------------|------|------|------|------|------|------| + /// | TIMWIN |`TIMB`|`TIMA`|`TIMD`|`TIMC`|`TIMF`|`TIME`| + WindowingResetToOtherCmp2OrCmp2UpToCmp3DownInUdm = 0b1111, +} + +impl Default for EevCfg { + fn default() -> Self { + Self { + _x: PhantomData, + filter_bits: EventFilter::None as u8, + latch_bit: false, + } + } +} + +impl Default for EevCfgs { + fn default() -> Self { + Self { + eev1: EevCfg::default(), + eev2: Default::default(), + eev3: Default::default(), + eev4: Default::default(), + eev5: Default::default(), + eev6: Default::default(), + eev7: Default::default(), + eev8: Default::default(), + eev9: Default::default(), + eev10: Default::default(), + event_counter_enable_bit: false, + event_counter_reset_mode_bit: false, + event_counter_source_bits: 0, + event_counter_threshold_bits: 0, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5441692a..6333bb93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ feature = "stm32g491", feature = "stm32g4a1" )))] - compile_error!( "This crate requires one of the following features enabled: stm32g431 @@ -81,6 +80,9 @@ pub mod dma; pub mod exti; pub mod flash; pub mod gpio; + +#[cfg(feature = "hrtim")] +pub mod hrtim; pub mod i2c; pub mod opamp; pub mod prelude; diff --git a/src/pwm.rs b/src/pwm.rs index 4cfd69e8..bee06e2a 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -172,6 +172,8 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; +use fugit::HertzU64; + use crate::hal; use crate::stm32::LPTIMER1; use crate::stm32::RCC; @@ -253,6 +255,7 @@ pub struct ComplementaryDisabled; pub struct ComplementaryEnabled; /// Enum for IO polarity +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Polarity { ActiveHigh, ActiveLow, @@ -1025,40 +1028,58 @@ pins! { ] } -// Period and prescaler calculator for 32-bit timers -// Returns (arr, psc) -fn calculate_frequency_32bit(base_freq: Hertz, freq: Hertz, alignment: Alignment) -> (u32, u16) { - let divisor = if let Alignment::Center = alignment { - freq * 2 - } else { - freq - }; +pub(crate) trait TimerType { + /// Returns (arr, psc) bits + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16); +} + +/// Any 32-bit timer +pub(crate) struct Timer32Bit; + +impl TimerType for Timer32Bit { + // Period and prescaler calculator for 32-bit timers + // Returns (arr, psc) bits + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16) { + let freq = HertzU64::from(freq); + let divisor = if let Alignment::Center = alignment { + freq * 2 + } else { + freq + }; + + // Round to the nearest period + let arr = (base_freq + (divisor / 2)) / divisor - 1; - // Round to the nearest period - let arr = (base_freq + (divisor / 2)) / divisor - 1; + assert!(arr <= u32::MAX as u64); - (arr, 0) + (arr as u32, 0) + } } -// Period and prescaler calculator for 16-bit timers -// Returns (arr, psc) -// Returns as (u32, u16) to be compatible but arr will always be a valid u16 -fn calculate_frequency_16bit(base_freq: Hertz, freq: Hertz, alignment: Alignment) -> (u32, u16) { - let ideal_period = calculate_frequency_32bit(base_freq, freq, alignment).0 + 1; +/// Any 16-bit timer except for HrTim +struct Timer16Bit; + +impl TimerType for Timer16Bit { + // Period and prescaler calculator for 16-bit timers + // Returns (arr, psc) + // Returns as (u32, u16) to be compatible but arr will always be a valid u16 + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16) { + let ideal_period = Timer32Bit::calculate_frequency(base_freq, freq, alignment).0 + 1; - // Division factor is (PSC + 1) - let prescale = (ideal_period - 1) / (1 << 16); + // Division factor is (PSC + 1) + let prescale = (ideal_period - 1) / (1 << 16); - // This will always fit in a 16-bit value because u32::MAX / (1 << 16) fits in a 16 bit + // This will always fit in a 16-bit value because u32::MAX / (1 << 16) fits in a 16 bit - // Round to the nearest period - let period = (ideal_period + (prescale >> 1)) / (prescale + 1) - 1; + // Round to the nearest period + let period = (ideal_period + (prescale >> 1)) / (prescale + 1) - 1; - // It should be impossible to fail these asserts - assert!(period <= 0xFFFF); - assert!(prescale <= 0xFFFF); + // It should be impossible to fail these asserts + assert!(period <= 0xFFFF); + assert!(prescale <= 0xFFFF); - (period, prescale as u16) + (period, prescale as u16) + } } // Deadtime calculator helper function @@ -1130,68 +1151,82 @@ pub trait PwmAdvExt: Sized { // Implement PwmExt trait for timer macro_rules! pwm_ext_hal { - ($TIMX:ident: $timX:ident) => { - impl PwmExt for $TIMX { + ($TIMX:ident: $timX:ident $(, $HrTimPsc:tt)*) => { + impl $(<$HrTimPsc>)* PwmExt for $TIMX { fn pwm(self, pins: PINS, frequency: T, rcc: &mut Rcc) -> PINS::Channel where PINS: Pins, T: Into, { - $timX(self, pins, frequency.into(), rcc) + $timX::<_, _, _ $(, $HrTimPsc )*>(self, pins, frequency.into(), rcc) } } }; } -// Implement PWM configuration for timer -macro_rules! tim_hal { - ($($TIMX:ident: ($timX:ident, - $typ:ty, $bits:expr $(, DIR: $cms:ident)* $(, BDTR: $bdtr:ident, $moe_set:ident, $af1:ident, $bkinp_setting:ident $(, $bk2inp_setting:ident)*)*),)+) => { - $( - pwm_ext_hal!($TIMX: $timX); +macro_rules! simple_tim_hal { + ($TIMX:ident: ( + $timX:ident, + $bits:ident, + $(, BDTR: $bdtr:ident, $moe_set:ident)* + )) => { + pwm_ext_hal!($TIMX: $timX); + + /// Configures PWM + fn $timX( + tim: $TIMX, + _pins: PINS, + freq: Hertz, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins<$TIMX, T, U>, + { + unsafe { + let rcc_ptr = &(*RCC::ptr()); + $TIMX::enable(rcc_ptr); + $TIMX::reset(rcc_ptr); + } - /// Configures PWM - fn $timX( - tim: $TIMX, - _pins: PINS, - freq: Hertz, - rcc: &mut Rcc, - ) -> PINS::Channel - where - PINS: Pins<$TIMX, T, U>, - { - unsafe { - let rcc_ptr = &(*RCC::ptr()); - $TIMX::enable(rcc_ptr); - $TIMX::reset(rcc_ptr); - } + let clk = $TIMX::get_timer_frequency(&rcc.clocks); - let clk = $TIMX::get_timer_frequency(&rcc.clocks); + let (period, prescale) = <$bits>::calculate_frequency(clk.into(), freq, Alignment::Left); - let (period, prescale) = match $bits { - 16 => calculate_frequency_16bit(clk, freq, Alignment::Left), - _ => calculate_frequency_32bit(clk, freq, Alignment::Left), - }; + // Write prescale + tim.psc.write(|w| { unsafe { w.psc().bits(prescale as u16) } }); - // Write prescale - tim.psc.write(|w| { unsafe { w.psc().bits(prescale as u16) } }); + // Write period + tim.arr.write(|w| { unsafe { w.arr().bits(period.into()) } }); - // Write period - tim.arr.write(|w| { unsafe { w.arr().bits(period.into()) } }); + // BDTR: Advanced-control timers + $( + // Set CCxP = OCxREF / CCxNP = !OCxREF + // Refer to RM0433 Rev 6 - Table 324. + tim.$bdtr.write(|w| + w.moe().$moe_set() + ); + )* - // BDTR: Advanced-control timers - $( - // Set CCxP = OCxREF / CCxNP = !OCxREF - // Refer to RM0433 Rev 6 - Table 324. - tim.$bdtr.write(|w| - w.moe().$moe_set() - ); - )* + tim.cr1.write(|w| w.cen().set_bit()); - tim.cr1.write(|w| w.cen().set_bit()); + unsafe { MaybeUninit::::uninit().assume_init() } + } + }; +} - unsafe { MaybeUninit::::uninit().assume_init() } - } +// Implement PWM configuration for timer +macro_rules! tim_hal { + ($($TIMX:ident: ( + $timX:ident, + $typ:ty, $bits:ident $( <$HrTimPsc:tt> )* $(, DIR: $cms:ident)* + $(, BDTR: $bdtr:ident, $moe_set:ident, $af1:ident, $bkinp_setting:ident $(, $bk2inp_setting:ident)*)* + ),)+) => { + $( + simple_tim_hal!($TIMX: ( + $timX, + $bits, + $(, BDTR: $bdtr, $moe_set)* + )); impl PwmAdvExt<$typ> for $TIMX { fn pwm_advanced( @@ -1238,10 +1273,7 @@ macro_rules! tim_hal { let (period, prescaler) = match self.count { CountSettings::Explicit { period, prescaler } => (period as u32, prescaler), CountSettings::Frequency( freq ) => { - match $bits { - 16 => calculate_frequency_16bit(self.base_freq, freq, self.alignment), - _ => calculate_frequency_32bit(self.base_freq, freq, self.alignment), - } + <$bits>::calculate_frequency(self.base_freq.into(), freq, self.alignment) }, }; @@ -1448,10 +1480,10 @@ macro_rules! tim_hal { } tim_hal! { - TIM1: (tim1, u16, 16, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), - TIM2: (tim2, u32, 32, DIR: cms), - TIM3: (tim3, u16, 16, DIR: cms), - TIM4: (tim4, u16, 16, DIR: cms), + TIM1: (tim1, u16, Timer16Bit, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), + TIM2: (tim2, u32, Timer32Bit, DIR: cms), + TIM3: (tim3, u16, Timer16Bit, DIR: cms), + TIM4: (tim4, u16, Timer16Bit, DIR: cms), } #[cfg(any( feature = "stm32g471", @@ -1461,13 +1493,13 @@ tim_hal! { feature = "stm32g484" ))] tim_hal! { - TIM5: (tim5, u32, 32, DIR: cms), + TIM5: (tim5, u32, Timer32Bit, DIR: cms), } tim_hal! { - TIM8: (tim8, u16, 16, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), - TIM15: (tim15, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), - TIM16: (tim16, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), - TIM17: (tim17, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), + TIM8: (tim8, u16, Timer16Bit, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), + TIM15: (tim15, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), + TIM16: (tim16, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), + TIM17: (tim17, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), } #[cfg(any( @@ -1479,7 +1511,7 @@ tim_hal! { feature = "stm32g4a1" ))] tim_hal! { - TIM20: (tim20, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), + TIM20: (tim20, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), } pub trait PwmPinEnable { diff --git a/src/rcc/config.rs b/src/rcc/config.rs index b53a280a..6939defa 100644 --- a/src/rcc/config.rs +++ b/src/rcc/config.rs @@ -46,6 +46,16 @@ pub enum PLLSrc { HSE_BYPASS(Hertz), } +impl PLLSrc { + pub const fn frequency(self) -> Hertz { + match self { + PLLSrc::HSI => Hertz::MHz(16), + PLLSrc::HSE(f) => f, + PLLSrc::HSE_BYPASS(f) => f, + } + } +} + /// Divider for the PLL clock input (M) /// This must be set based on the input clock to keep the PLL input frequency within the limits /// specified in the datasheet. @@ -70,11 +80,11 @@ pub enum PllMDiv { } impl PllMDiv { - pub fn divisor(&self) -> u32 { + pub const fn divisor(&self) -> u32 { (*self as u32) + 1 } - pub fn register_setting(&self) -> u8 { + pub const fn register_setting(&self) -> u8 { *self as u8 } } @@ -108,11 +118,11 @@ pub enum PllRDiv { } impl PllRDiv { - pub fn divisor(&self) -> u32 { + pub const fn divisor(&self) -> u32 { ((*self as u32) + 1) * 2 } - pub fn register_setting(&self) -> u8 { + pub const fn register_setting(&self) -> u8 { *self as u8 } } @@ -157,11 +167,11 @@ pub enum PllPDiv { } impl PllPDiv { - pub fn divisor(&self) -> u32 { + pub const fn divisor(&self) -> u32 { *self as u32 } - pub fn register_setting(&self) -> u8 { + pub const fn register_setting(&self) -> u8 { *self as u8 } } @@ -292,7 +302,7 @@ pub enum PllNMul { } impl PllNMul { - pub fn multiplier(&self) -> u32 { + pub const fn multiplier(&self) -> u32 { *self as u32 } diff --git a/src/rcc/enable.rs b/src/rcc/enable.rs index 82e61c70..ff3052b9 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -207,10 +207,5 @@ bus! { #[cfg(any(feature = "stm32g474", feature = "stm32g484"))] bus! { - HRTIM_TIMA => (APB2, 26), - HRTIM_TIMB => (APB2, 26), - HRTIM_TIMC => (APB2, 26), - HRTIM_TIMD => (APB2, 26), - HRTIM_TIME => (APB2, 26), - HRTIM_TIMF => (APB2, 26), + HRTIM_COMMON => (APB2, 26), }