diff --git a/Cargo.toml b/Cargo.toml index 8bac4fc7..dfaa025a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,7 @@ version = "0.0.2" [dependencies] nb = "0.1.1" -#stm32g4 = { git = "https://github.com/stm32-rs/stm32-rs-nightlies" } #"0.15.1" -stm32g4 = { version = "0.17.0", package = "stm32g4-staging" } +stm32g4 = { version = "0.19.0", package = "stm32g4-staging" } paste = "1.0" bitflags = "1.2" vcell = "0.1" @@ -22,6 +21,7 @@ static_assertions = "1.1" fugit = "0.3.5" stm32-usbd = { version = "0.7.0", optional = true } fixed = { version = "1.28.0", optional = true } +embedded-io = "0.6.1" [dependencies.cortex-m] version = "0.7.7" @@ -86,11 +86,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"] @@ -120,3 +121,48 @@ required-features = ["usb"] [[example]] name = "cordic" required-features = ["cordic"] + +[[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-capture-dma" +required-features = ["hrtim"] +path = "examples/hrtim/capture-dma.rs" + +[[example]] +name = "hrtim-eev-comp" +required-features = ["hrtim"] +path = "examples/hrtim/eev-comp.rs" + +[[example]] +name = "hrtim-eev" +required-features = ["hrtim"] +path = "examples/hrtim/eev.rs" + +[[example]] +name = "hrtim-flt-comp" +required-features = ["hrtim"] +path = "examples/hrtim/flt-comp.rs" + +[[example]] +name = "hrtim-flt" +required-features = ["hrtim"] +path = "examples/hrtim/flt.rs" + +[[example]] +name = "hrtim" +required-features = ["hrtim"] +path = "examples/hrtim/hrtim.rs" + +[[example]] +name = "hrtim-master" +required-features = ["hrtim"] +path = "examples/hrtim/master.rs" \ No newline at end of file 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..e03c8f7a --- /dev/null +++ b/examples/hrtim/adc-trigger.rs @@ -0,0 +1,165 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +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::GpioExt, + 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 = gpioa.pa8; + let pin_b = gpioa.pa9; + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + // . ^ ^ + // . | | + //AD samlp pa0 temp + let period = 0xFFFF; + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut cr3, + mut cr4, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &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.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..a1ce1ffc --- /dev/null +++ b/examples/hrtim/capture-dma.rs @@ -0,0 +1,141 @@ +#![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 stm32g4xx_hal::hrtim::{timer::TimerSplitCapture, HrParts}; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use stm32g4xx_hal as hal; + + use hal::{ + dma::{config::DmaConfig, stream::DMAExt, TransferExt}, + gpio::GpioExt, + 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 = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // t1 t2 . + // | | . + // v v . + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Both, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + timer, + mut cr1, + out: mut out1, + dma_channel, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &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 TimerSplitCapture { + mut timer, + ch1: mut capture, + .. + } = timer.split_capture(); + timer.start(&mut hr_control.control); + out1.enable(); + + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + info!("Setup DMA"); + let 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_channel), + &mut first_buffer[..], + config, + ); + + transfer.start(|_| ()); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + let mut data = [0; 2]; + transfer.read_exact(&mut data); + let [t1, t2] = data.map(|x| capture::dma_value_to_signed(x, period)); + cr1.set_duty(duty as u16); + info!("Capture: t1: {}, t2: {}, duty: {}, ", t1, t2, old_duty); + old_duty = duty; + } + } +} diff --git a/examples/hrtim/capture.rs b/examples/hrtim/capture.rs new file mode 100644 index 00000000..e2416396 --- /dev/null +++ b/examples/hrtim/capture.rs @@ -0,0 +1,121 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use stm32g4xx_hal as hal; + + use hal::{ + gpio::GpioExt, + 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 = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Falling, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + + cr1.set_duty(period / 2); + timer.start(&mut hr_control.control); + out.enable(); + + let capture = timer.capture_ch1(); + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + if let Some(value) = capture.get_signed(period) { + info!( + "Capture: {:?}, duty: {}, diff: {}", + value, + old_duty, + value - old_duty as i32 + ); + cr1.set_duty(duty as u16); + old_duty = duty; + } + } + } +} diff --git a/examples/hrtim/eev-comp.rs b/examples/hrtim/eev-comp.rs new file mode 100644 index 00000000..b405840a --- /dev/null +++ b/examples/hrtim/eev-comp.rs @@ -0,0 +1,141 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +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::SignalEdge; + 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 = gpioa.pa8; + + 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 HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default().eev4(EevCfg::default())) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input4); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + info!( + "Comp: {}, pending: {}", + comp1.output(), + comp1.is_pending(&exti) + ); + } +} diff --git a/examples/hrtim/eev.rs b/examples/hrtim/eev.rs new file mode 100644 index 00000000..390461f0 --- /dev/null +++ b/examples/hrtim/eev.rs @@ -0,0 +1,109 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + 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 = gpioa.pa8; + + // . . * . + // . 33% . * . . . + // .-----. .--* .-----. .-----. .----- + //out1 | | | | | | | | | + // | | | * | | | | | + // ------ ----------- -------------- ----------- ----------- + // . . * . . . + // . . * . . . + // . . *--------* . . . + //eev . . | | . . . + // . . | | . . . + // ------------------------- ------------------------------------------ + // . . * . . . + // . . * . . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a, &mut rcc) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default()) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input3); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + cortex_m::asm::nop() + } +} diff --git a/examples/hrtim/flt-comp.rs b/examples/hrtim/flt-comp.rs new file mode 100644 index 00000000..e99448dd --- /dev/null +++ b/examples/hrtim/flt-comp.rs @@ -0,0 +1,153 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::comparator::{ComparatorExt, ComparatorSplit, Config, Hysteresis}; + use hal::dac::{Dac3IntSig1, DacExt, DacOut}; + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::fault::{FaultAction, FaultMonitor}; + 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::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 = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + out: 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.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..2408aa54 --- /dev/null +++ b/examples/hrtim/flt.rs @@ -0,0 +1,121 @@ +#![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 stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use hal::hrtim::compare_register::HrCompareRegister; + use hal::hrtim::fault::{FaultAction, FaultMonitor}; + 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::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 = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = 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); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + //unsafe {((HRTIM_COMMON::ptr() as *mut u8).offset(0x14) as *mut u32).write_volatile(1); } + out.enable(); + timer.start(&mut hr_control.control); + + info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + info!("State: {:?}", out.get_state()); + } + if hr_control.fault_3.is_fault_active() { + hr_control.fault_3.clear_fault(); // Clear fault every 5s + out.enable(); + info!("failt cleared, and output reenabled"); + } + } +} diff --git a/examples/hrtim/hrtim.rs b/examples/hrtim/hrtim.rs new file mode 100644 index 00000000..bac0ff7a --- /dev/null +++ b/examples/hrtim/hrtim.rs @@ -0,0 +1,109 @@ +#![no_std] +#![no_main] + +/// Add description + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; +use stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use fugit::ExtU32; + 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 = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &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..5e5793b7 --- /dev/null +++ b/examples/hrtim/master.rs @@ -0,0 +1,136 @@ +#![no_std] +#![no_main] + +/// Add description + +#[path = "../utils/mod.rs"] +mod utils; + +use cortex_m_rt::entry; +use stm32g4xx_hal::hrtim::HrParts; +use utils::logger::info; + +#[entry] +fn main() -> ! { + use fugit::ExtU32; + 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 = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &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 HrParts { + timer: mut mtimer, + cr1: mut mcr1, + .. + } = 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(); + + 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/uart-dma-tx.rs b/examples/uart-dma-tx.rs index 78abeb4f..2f56eea0 100644 --- a/examples/uart-dma-tx.rs +++ b/examples/uart-dma-tx.rs @@ -5,9 +5,9 @@ extern crate cortex_m_rt as rt; -use core::fmt::Write; - -use hal::dma::{config::DmaConfig, stream::DMAExt, TransferExt}; +use core::fmt::Write as _; +use embedded_io::Write as _; +use hal::dma::stream::DMAExt; use hal::prelude::*; use hal::pwr::PwrExt; use hal::serial::*; @@ -33,10 +33,6 @@ fn main() -> ! { let mut rcc = rcc.freeze(rcc::Config::hsi(), pwr); let streams = dp.DMA1.split(&rcc); - let config = DmaConfig::default() - .transfer_complete_interrupt(false) - .circular_buffer(false) - .memory_increment(true); info!("Init UART"); let gpioa = dp.GPIOA.split(&mut rcc); @@ -53,28 +49,44 @@ fn main() -> ! { .unwrap(); let mut delay_syst = cp.SYST.delay(&rcc.clocks); - let mut led = gpioa.pa5.into_push_pull_output(); + let mut led1 = gpioa.pa5.into_push_pull_output(); + let mut led2 = gpioa.pa6.into_push_pull_output(); info!("Start writing"); writeln!(usart, "Hello without DMA\r").unwrap(); - let tx_buffer = cortex_m::singleton!(: [u8; 17] = *b"Hello with DMA!\r\n").unwrap(); + let tx_buffer = cortex_m::singleton!(: [u8; 64] = [0; 64]).unwrap(); let (tx, _rx) = usart.split(); // Setup DMA for USART2 TX with dma channel 1. - let mut transfer = - streams - .0 - .into_memory_to_peripheral_transfer(tx.enable_dma(), &mut tx_buffer[..], config); + let mut tx = tx.enable_dma(streams.0, &mut tx_buffer[..]); - transfer.start(|_tx| {}); loop { - while !transfer.get_transfer_complete_flag() {} - - delay_syst.delay(1000.millis()); - led.toggle().unwrap(); - transfer.restart(|_tx| {}); + for i in 0.. { + // This will write to the DMA buffer and return directly, proveded the buffer is empty + // and there is enough room in the buffer for the entire message. Otherwise it will block. + led1.set_high().unwrap(); + writeln!(&mut tx, "Hello with DMA").unwrap(); + led1.set_low().unwrap(); + + delay_syst.delay(100.millis()); + + // This will not block due the message above since we have a delay. However do watch out for the + // `{i}` since this will invoke another DMA transfer. This will block until the first part of + // the message before the `{i}` has been flushed. Same thing for the part after the `{i}`. + led2.set_high().unwrap(); + writeln!(&mut tx, "Partially blocking DMA write: {i}!").unwrap(); + led2.set_low().unwrap(); + + // Something like this ensures there is only one DMA transfer. This, assuming the DMA buffer is empty and + // the data fits, guarantees a non blocking write: + //let mut bytes = heapless::Vec::<_, 64>::new(); + //writeln!(&mut bytes, "Non blocking write of complex message: {i} % 10 = {}!", i % 10); + //tx.write(bytes.as_slice()).unwrap(); + + delay_syst.delay(1000.millis()); + } } } diff --git a/src/adc.rs b/src/adc.rs index 600e8bcf..45f775bf 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1885,6 +1885,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() ); @@ -2336,8 +2341,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/comparator.rs b/src/comparator.rs index b5e80fc4..8b5f2851 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -56,9 +56,8 @@ impl EnabledState for Locked {} macro_rules! impl_comp { ($($t:ident: $reg:ident,)+) => {$( - pub struct $t { - _rb: PhantomData<()>, - } + #[non_exhaustive] + pub struct $t; impl $t { pub fn csr(&self) -> &$crate::stm32::comp::CCSR { @@ -548,31 +547,31 @@ pub fn split(_comp: COMP, rcc: &mut Rcc) -> Comparators { rcc.rb.apb2rstr().modify(|_, w| w.syscfgrst().clear_bit()); ( - COMP1 { _rb: PhantomData }, - COMP2 { _rb: PhantomData }, - COMP3 { _rb: PhantomData }, - COMP4 { _rb: PhantomData }, + COMP1, + COMP2, + COMP3, + COMP4, #[cfg(any( feature = "stm32g473", feature = "stm32g483", feature = "stm32g474", feature = "stm32g484" ))] - COMP5 { _rb: PhantomData }, + COMP5, #[cfg(any( feature = "stm32g473", feature = "stm32g483", feature = "stm32g474", feature = "stm32g484" ))] - COMP6 { _rb: PhantomData }, + COMP6, #[cfg(any( feature = "stm32g473", feature = "stm32g483", feature = "stm32g474", feature = "stm32g484" ))] - COMP7 { _rb: PhantomData }, + COMP7, ) } diff --git a/src/dma/config.rs b/src/dma/config.rs index 21b3bd02..6c854c58 100644 --- a/src/dma/config.rs +++ b/src/dma/config.rs @@ -38,7 +38,6 @@ pub struct DmaConfig { pub(crate) transfer_complete_interrupt: bool, pub(crate) half_transfer_interrupt: bool, pub(crate) transfer_error_interrupt: bool, - pub(crate) double_buffer: bool, } impl DmaConfig { @@ -84,10 +83,4 @@ impl DmaConfig { self.transfer_error_interrupt = transfer_error_interrupt; self } - /// Set the double_buffer. - #[inline(always)] - pub fn double_buffer(mut self, double_buffer: bool) -> Self { - self.double_buffer = double_buffer; - self - } } diff --git a/src/dma/transfer.rs b/src/dma/transfer.rs index 2c0d2f3d..399abe87 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 @@ -512,7 +536,7 @@ impl_adc_overrun!(ADC4, ADC5,); macro_rules! impl_serial_timeout { ($($uart:ident, )*) => {$( - impl CircTransfer, BUF> + impl CircTransfer, BUF> where STREAM: Stream, /*BUF: StaticWriteBuffer + Deref*/ { diff --git a/src/hrtim/adc_trigger.rs b/src/hrtim/adc_trigger.rs new file mode 100644 index 00000000..6238501f --- /dev/null +++ b/src/hrtim/adc_trigger.rs @@ -0,0 +1,23 @@ +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; +} + +/// Handle to timers reset/roll-over event +pub struct TimerReset(pub(crate) PhantomData); + +/// Handle to timers period event +pub struct TimerPeriod(pub(crate) PhantomData); diff --git a/src/hrtim/capture.rs b/src/hrtim/capture.rs new file mode 100644 index 00000000..eb1d0475 --- /dev/null +++ b/src/hrtim/capture.rs @@ -0,0 +1,265 @@ +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; + +/// Type alias for the default capture for channel 1 +pub type HrCaptCh1 = HrCapt; + +/// Type alias for the default capture for channel 2 +pub type HrCaptCh2 = HrCapt; + +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: +/// * All neighbor timers CMP1, CPM2, OUT1_RST and OUT1_SET events +pub trait CaptureEvent { + const BITS: u32; +} + +/// Trait for capture channels used for capturing edges +/// +/// ``` +/// let capture: HrCapt<_, _, _> = todo!(); +/// if capture.is_pending() { +/// let (value, dir) = capture.get_last(); +/// capture.clear_interrupt(); +/// defmt::info!("Edge captured at counter value: {}, with: {}", value, dir); +/// } +/// ``` +/// +/// or alternatively +/// +/// ``` +/// let capture: HrCapt<_, _, _> = todo!(); +/// if let Some((value, dir)) = capture.get() { +/// defmt::info!("Edge captured at counter value: {}, with: {}", value, dir); +/// } +/// ``` +pub trait HrCapture { + /// Try to get the capture value + /// + /// Returns none if edge has been captured since last time + /// + /// NOTE: This function will use [`Self::is_pending`] to chech if there is a value available and + /// [`Self::clear_interrupt`] to clear it. + fn get(&mut self) -> Option<(u16, CountingDirection)> { + if self.is_pending() { + let value = self.get_last(); + self.clear_interrupt(); + Some(value) + } else { + None + } + } + + /// Get number of ticks relative to beginning of upcounting + /// + /// where captures during down counting count as negative (before the upcount) + /// + /// ```txt + /// Counter + /// ---------------------------------- <--- period + /// \ ^ / + /// \ | / + /// \ | / + /// \ | / + /// Down count \ | / Up count + /// \|/ + /// <-------------- 0 --------------> t + /// Negative result | positive result + /// ``` + /// + /// NOTE: This function will use [`Self::is_pending`] to chech if there is a value available and + /// [`Self::clear_interrupt`] to clear it. + fn get_signed(&mut self, period: u16) -> Option { + if self.is_pending() { + let value = self.get_last_signed(period); + self.clear_interrupt(); + Some(value) + } else { + None + } + } + + fn get_last(&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_last_signed(&self, period: u16) -> i32 { + let (value, dir) = self.get_last(); + + // The capture counter always counts up and restarts at period + match dir { + CountingDirection::Up => i32::from(value), + CountingDirection::Down => i32::from(value) - i32::from(period), + } + } + + 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, period: u16) -> i32 { + let (value, dir) = dma_value_to_dir_and_value(x); + + // The capture counter always counts up and restarts at period + match dir { + CountingDirection::Up => i32::from(value), + CountingDirection::Down => i32::from(value) - i32::from(period), + } +} + +macro_rules! impl_capture { + ($($TIMX:ident),+) => {$( + impl_capture!($TIMX: Ch1, cpt1r, cpt1cr, cpt1, cpt1ie, cpt1de, cpt1c); + impl_capture!($TIMX: Ch2, cpt2r, cpt2cr, cpt2, cpt2ie, cpt2de, cpt2c); + )+}; + + ($TIMX:ident: $CH:ident, $cptXr:ident, $cptXcr:ident, $cptX:ident, $cptXie:ident, $cptXde:ident, $cptXc:ident) => { + 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.$cptXcr().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.$cptXcr().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.$cptXcr().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_last(&self) -> (u16, CountingDirection) { + let tim = unsafe { &*$TIMX::ptr() }; + let data = tim.$cptXr().read(); + + let dir = match data.dir().bit() { + true => CountingDirection::Down, + false => CountingDirection::Up, + }; + let value = data.cpt().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().clear()); + } + + 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.$cptXr() as *const _ as u32 + } + + type MemSize = u32; + + const REQUEST_LINE: Option = Some(DmaMuxResources::$TIMX as u8); + } + }; +} + +impl_capture! { + HRTIM_TIMA, + HRTIM_TIMB, + HRTIM_TIMC, + HRTIM_TIMD, + HRTIM_TIME, + HRTIM_TIMF +} diff --git a/src/hrtim/compare_register.rs b/src/hrtim/compare_register.rs new file mode 100644 index 00000000..0b54c741 --- /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, + [$(($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, [$(($Trigger: $trigger_bits)),*], [$(($event_dst, $tim_event_index)),*]); + }; + + ($TIMX:ident: $cr_type:ident: + $cmpXYr: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().cmp().bits() + } + fn set_duty(&mut self, duty: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr().write(|w| unsafe { w.cmp().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: [ + [$(($cr1_trigger:ident: $cr1_trigger_bits:expr)),*], [$(($cr1_event_dst:ident, $cr1_tim_event_index:expr)),*], + [$(($cr2_trigger:ident: $cr2_trigger_bits:expr)),*], [$(($cr2_event_dst:ident, $cr2_tim_event_index:expr)),*], + [$(($cr3_trigger:ident: $cr3_trigger_bits:expr)),*], [$(($cr3_event_dst:ident, $cr3_tim_event_index:expr)),*], + [$(($cr4_trigger:ident: $cr4_trigger_bits:expr)),*], [$(($cr4_event_dst:ident, $cr4_tim_event_index:expr)),*] + ]),+) => {$( + hrtim_cr_helper!($TIMX: HrCr1: cmp1r, [$(($cr1_trigger: $cr1_trigger_bits)),*], [$(($cr1_event_dst, $cr1_tim_event_index)),*], 3); + hrtim_cr_helper!($TIMX: HrCr2: cmp2r, [$(($cr2_trigger: $cr2_trigger_bits)),*], [$(($cr2_event_dst, $cr2_tim_event_index)),*], 4); + hrtim_cr_helper!($TIMX: HrCr3: cmp3r, [$(($cr3_trigger: $cr3_trigger_bits)),*], [$(($cr3_event_dst, $cr3_tim_event_index)),*], 5); + hrtim_cr_helper!($TIMX: HrCr4: cmp4r, [$(($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: [ + [(Adc13: 1 << 0), (Adc24: 1 << 0), (Adc579: 0), (Adc6810: 0) ], [], + [(Adc13: 1 << 1), (Adc24: 1 << 1), (Adc579: 1), (Adc6810: 1) ], [], + [(Adc13: 1 << 2), (Adc24: 1 << 2), (Adc579: 2), (Adc6810: 2) ], [], + [(Adc13: 1 << 3), (Adc24: 1 << 3), (Adc579: 3), (Adc6810: 3) ], [] + ], + + HRTIM_TIMA: [ + [ ], [(HRTIM_TIMB, 1), (HRTIM_TIMD, 1) ], + [ (Adc24: 1 << 10), (Adc6810: 10)], [(HRTIM_TIMB, 2), (HRTIM_TIMC, 1) ], + [(Adc13: 1 << 11), (Adc579: 10) ], [(HRTIM_TIMC, 2), (HRTIM_TIMF, 1) ], + [(Adc13: 1 << 12), (Adc24: 1 << 12), (Adc579: 11), (Adc6810: 11)], [(HRTIM_TIMD, 2), (HRTIM_TIME, 1) ] + ], + + HRTIM_TIMB: [ + [ ], [(HRTIM_TIMA, 1), (HRTIM_TIMF, 2) ], + [ (Adc24: 1 << 14), (Adc6810: 13)], [(HRTIM_TIMA, 2), (HRTIM_TIMC, 3), (HRTIM_TIMD, 3)], + [(Adc13: 1 << 16), (Adc579: 14) ], [(HRTIM_TIMC, 4), (HRTIM_TIME, 2) ], + [(Adc13: 1 << 17), (Adc24: 1 << 16), (Adc579: 15), (Adc6810: 14)], [(HRTIM_TIMD, 4), (HRTIM_TIME, 3), (HRTIM_TIMF, 3)] + ], + + HRTIM_TIMC: [ + [ ], [(HRTIM_TIME, 4), (HRTIM_TIMF, 4) ], + [ (Adc24: 1 << 18), (Adc6810: 16)], [(HRTIM_TIMA, 3), (HRTIM_TIME, 5) ], + [(Adc13: 1 << 21), (Adc579: 18) ], [(HRTIM_TIMA, 4), (HRTIM_TIMB, 3) ], + [(Adc13: 1 << 22), (Adc24: 1 << 20), (Adc579: 19), (Adc6810: 17)], [(HRTIM_TIMB, 4), (HRTIM_TIMD, 5), (HRTIM_TIMF, 5)] + ], + + HRTIM_TIMD: [ + [ ], [(HRTIM_TIMA, 5), (HRTIM_TIME, 6) ], + [ (Adc24: 1 << 23), (Adc6810: 20)], [(HRTIM_TIMA, 6), (HRTIM_TIMC, 5), (HRTIM_TIME, 7)], + [(Adc13: 1 << 25), (Adc579: 21) ], [(HRTIM_TIMB, 5), (HRTIM_TIMF, 6) ], + [(Adc13: 1 << 26), (Adc24: 1 << 25), (Adc579: 22), (Adc6810: 21)], [(HRTIM_TIMB, 6), (HRTIM_TIMC, 6), (HRTIM_TIMF, 7)] + ], + + HRTIM_TIME: [ + [ ], [(HRTIM_TIMB, 7), (HRTIM_TIMD, 6) ], + [ (Adc24: 1 << 28), (Adc6810: 24)], [(HRTIM_TIMB, 8), (HRTIM_TIMF, 8) ], + [(Adc13: 1 << 29), (Adc24: 1 << 29), (Adc579: 24), (Adc6810: 25)], [(HRTIM_TIMA, 7), (HRTIM_TIMC, 7), (HRTIM_TIMF, 9)], + [(Adc13: 1 << 30), (Adc24: 1 << 30), (Adc579: 25), (Adc6810: 26)], [(HRTIM_TIMA, 8), (HRTIM_TIMC, 8), (HRTIM_TIMD, 7)] + ], + + HRTIM_TIMF: [ + [ (Adc24: 1 << 15) ], [(HRTIM_TIMD, 8) ], + [(Adc13: 1 << 10), (Adc24: 1 << 11), (Adc579: 27), (Adc6810: 28)], [(HRTIM_TIMC, 9) ], + [(Adc13: 1 << 15), (Adc579: 28), (Adc6810: 29)], [(HRTIM_TIMB, 9), (HRTIM_TIMD, 9), (HRTIM_TIME, 8)], + [(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..57974d69 --- /dev/null +++ b/src/hrtim/control.rs @@ -0,0 +1,377 @@ +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, 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. +#[non_exhaustive] +pub struct HrTimCalibrated; + +impl HrTimCalibrated { + pub fn constrain(self) -> HrPwmControl { + HrPwmControl { + control: HrPwmCtrl, + fault_sys: FltMonitorSys, + fault_1: FltMonitor1, + fault_2: FltMonitor2, + fault_3: FltMonitor3, + fault_4: FltMonitor4, + fault_5: FltMonitor5, + fault_6: FltMonitor6, + + adc_trigger1: Adc1Trigger, + adc_trigger2: Adc2Trigger, + adc_trigger3: Adc3Trigger, + adc_trigger4: Adc4Trigger, + adc_trigger5: Adc5Trigger, + adc_trigger6: Adc6Trigger, + adc_trigger7: Adc7Trigger, + adc_trigger8: Adc8Trigger, + adc_trigger9: Adc9Trigger, + adc_trigger10: Adc10Trigger, + } + } +} + +impl<'a> From<&'a mut HrPwmControl> for &'a mut HrPwmCtrl { + fn from(val: &'a mut HrPwmControl) -> Self { + &mut val.control + } +} + +/// Used as a token to guarantee unique access to resources common to multiple timers +/// +/// An instance of this object can be obtained from [`HrPwmControl`].control +#[non_exhaustive] +pub struct HrPwmCtrl; + +/// Used as a token to guarantee unique access to resources common to multiple timers +#[non_exhaustive] +pub struct HrPwmControl { + pub control: HrPwmCtrl, + + 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)*]),*) => {$( + #[non_exhaustive] + pub struct $t; + + 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]),*) => {$( + #[non_exhaustive] + pub struct $t; + + impl $t { + pub fn enable_source(&mut self, _trigger: &T) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common + .adcer() + .modify(|_r, w| unsafe { w.$adcXtrg().bits(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..29a1f7b9 --- /dev/null +++ b/src/hrtim/event.rs @@ -0,0 +1,17 @@ +/// Event that can be used to set/reset an output +pub trait EventSource { + const BITS: u32; +} + +/// Event that can be used reset the timer counter +/// +/// Done: +/// * [x] Eev1-10 +/// * [x] Master period +/// * [x] Master CMP1-4 +/// * [x] Cmp2, Cmp4 +/// * [x] Timer Update +/// * [ ] Neighbor timers compare events +pub trait TimerResetEventSource { + const BITS: u32; +} diff --git a/src/hrtim/event.rs_old b/src/hrtim/event.rs_old new file mode 100644 index 00000000..65258c1c --- /dev/null +++ b/src/hrtim/event.rs_old @@ -0,0 +1,619 @@ +use core::marker::PhantomData; + +use crate::stm32::{ + HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF, +}; + +use super::{compare_register::{HrCr1, HrCr2, HrCr3, HrCr4}, external_event_old::ExternalEventSource}; +use crate::hrtim::timer::HrTim; + +macro_rules! impl_into_es { + ($dst:ident: [$(($t:ty, $ES:ident),)*]) => {$( + impl_into_es!($dst, $t, $ES); + )*}; + + ($dst:ident, $t:ty, $ES:ident) => { + impl From<&$t> for EventSource { + fn from(_: &$t) -> Self { + EventSource::$ES{ _x: PhantomData } + } + } + }; + ($dst:ident) => { + impl_into_es! { + $dst: [ + (HrCr1<$dst, PSCL>, Cr1), + (HrCr2<$dst, PSCL>, Cr2), + (HrCr3<$dst, PSCL>, Cr3), + (HrCr4<$dst, PSCL>, Cr4), + (HrTim<$dst, PSCL>, Period), + + (HrCr1, MasterCr1), + (HrCr2, MasterCr2), + (HrCr3, MasterCr3), + (HrCr4, MasterCr4), + (HrTim, MasterPeriod), + ] + } + }; +} + +impl_into_es!(HRTIM_TIMA); +impl_into_es!(HRTIM_TIMB); +impl_into_es!(HRTIM_TIMC); +impl_into_es!(HRTIM_TIMD); +impl_into_es!(HRTIM_TIME); +impl_into_es!(HRTIM_TIMF); + +macro_rules! impl_into_neighbor_es { + ( + DST: $dst:ident: [ + ($src1:ident, $cr1:ident), + ($src2:ident, $cr2:ident), + ($src3:ident, $cr3:ident), + ($src4:ident, $cr4:ident), + ($src5:ident, $cr5:ident), + ($src6:ident, $cr6:ident), + ($src7:ident, $cr7:ident), + ($src8:ident, $cr8:ident), + ($src9:ident, $cr9:ident), + ] + ) => { + impl_into_neighbor_es!($dst, $src1, $cr1, TimEvent1); + impl_into_neighbor_es!($dst, $src2, $cr2, TimEvent2); + impl_into_neighbor_es!($dst, $src3, $cr3, TimEvent3); + impl_into_neighbor_es!($dst, $src4, $cr4, TimEvent4); + impl_into_neighbor_es!($dst, $src5, $cr5, TimEvent5); + impl_into_neighbor_es!($dst, $src6, $cr6, TimEvent6); + impl_into_neighbor_es!($dst, $src7, $cr7, TimEvent7); + impl_into_neighbor_es!($dst, $src8, $cr8, TimEvent8); + impl_into_neighbor_es!($dst, $src9, $cr9, TimEvent9); + }; + + ($dst:ident, $src:ident, $cr:ident, $TimEventX:ident) => { + impl From<&$cr<$src, PSCL>> for EventSource { + fn from(_: &$cr<$src, PSCL>) -> Self { + EventSource::NeighborTimer { + n: NeighborTimerEventSource::$TimEventX { _x: PhantomData }, + } + } + } + }; +} + +impl_into_neighbor_es! { + DST: HRTIM_TIMA: [ + // src + (HRTIM_TIMB, HrCr1), + (HRTIM_TIMB, HrCr2), + (HRTIM_TIMC, HrCr2), + (HRTIM_TIMC, HrCr3), + (HRTIM_TIMD, HrCr1), + (HRTIM_TIMD, HrCr2), + (HRTIM_TIME, HrCr3), + (HRTIM_TIME, HrCr4), + (HRTIM_TIMF, HrCr4), + ] +} + +impl_into_neighbor_es! { + DST: HRTIM_TIMB: [ + // src + (HRTIM_TIMA, HrCr1), + (HRTIM_TIMA, HrCr2), + (HRTIM_TIMC, HrCr3), + (HRTIM_TIMC, HrCr4), + (HRTIM_TIMD, HrCr3), + (HRTIM_TIMD, HrCr4), + (HRTIM_TIME, HrCr1), + (HRTIM_TIME, HrCr2), + (HRTIM_TIMF, HrCr3), + ] +} + +impl_into_neighbor_es! { + DST: HRTIM_TIMC: [ + // src + (HRTIM_TIMA, HrCr2), + (HRTIM_TIMA, HrCr3), + (HRTIM_TIMB, HrCr2), + (HRTIM_TIMB, HrCr3), + (HRTIM_TIMD, HrCr2), + (HRTIM_TIMD, HrCr4), + (HRTIM_TIME, HrCr3), + (HRTIM_TIME, HrCr4), + (HRTIM_TIMF, HrCr2), + ] +} + +// TODO: Continue for TIMD, TIME and TIMF, see RM0440 Table 218. 'Events mapping across timer A to F' + +pub enum EventSource { + /// Compare match with compare register 1 of this timer + Cr1 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 2 of this timer + Cr2 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 3 of this timer + Cr3 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 4 of this timer + Cr4 { _x: PhantomData<(PSCL, DST)> }, + + /// On complete period + Period { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 1 of master timer + MasterCr1 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 2 of master timer + MasterCr2 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 3 of master timer + MasterCr3 { _x: PhantomData<(PSCL, DST)> }, + + /// Compare match with compare register 4 of master timer + MasterCr4 { _x: PhantomData<(PSCL, DST)> }, + + /// On complete master period + MasterPeriod { _x: PhantomData<(PSCL, DST)> }, + + ExternalEvent(ExternalEventSource), // This is fine + + NeighborTimer { + n: NeighborTimerEventSource, + }, +} + +/// Compare events from neighbor timers +/// +/// See RM0440 Table 218. 'Events mapping across timer A to F' +pub enum NeighborTimerEventSource { + /// Timer event 1 + /// + /// This is different depending on destination timer: + /// |dest | source | + /// |-----|--------| + /// |TimA | B CR1 | + /// |TimB | A CR1 | + /// |TimC | A CR2 | + /// |TimD | A CR1 | + /// |TimE | A CR4 | + /// |TimF | A CR3 | + TimEvent1 { + _x: PhantomData<(PSCL, DST)>, + }, + + /// Timer event x + /// + /// This is different depending on destination timer: + /// |dest | source | + /// |-----|--------| + /// |TimA | x CRy | + /// |TimB | x CRy | + /// |TimC | x CRy | + /// |TimD | x CRy | + /// |TimE | x CRy | + /// |TimF | x CRy | + //TimEventx, + + /// Timer event 2 + /// + /// This is different depending on destination timer: + /// |dest | source | + /// |-----|--------| + /// |TimA | B CR2 | + /// |TimB | A CR2 | + /// |TimC | A CR3 | + /// |TimD | A CR4 | + /// |TimE | B CR3 | + /// |TimF | B CR1 | + TimEvent2 { + _x: PhantomData<(PSCL, DST)>, + }, + + /// Timer event 3 + /// + /// This is different depending on destination timer: + /// |dest | source | + /// |-----|--------| + /// |TimA | C CR2 | + /// |TimB | C CR3 | + /// |TimC | B CR2 | + /// |TimD | B CR2 | + /// |TimE | B CR4 | + /// |TimF | B CR4 | + TimEvent3 { + _x: PhantomData<(PSCL, DST)>, + }, + + /// Timer event 4 + /// + /// This is different depending on destination timer: + /// |dest | source | + /// |-----|--------| + /// |TimA | C CR3 | + /// |TimB | C CR4 | + /// |TimC | B CR3 | + /// |TimD | B CR4 | + /// |TimE | C CR1 | + /// |TimF | C CR1 | + TimEvent4 { + _x: PhantomData<(PSCL, DST)>, + }, + // TODO: Document those + TimEvent5 { + _x: PhantomData<(PSCL, DST)>, + }, + TimEvent6 { + _x: PhantomData<(PSCL, DST)>, + }, + TimEvent7 { + _x: PhantomData<(PSCL, DST)>, + }, + TimEvent8 { + _x: PhantomData<(PSCL, DST)>, + }, + TimEvent9 { + _x: PhantomData<(PSCL, DST)>, + }, +} + +macro_rules! hr_timer_reset_event_source_common { + ($(#[$($attrss:tt)*])* pub enum $t:ident { [COMMON], $(#[$($attrss2:tt)*] $vals:tt = 1 << $x:literal,)*}) => { + $(#[$($attrss)*])* + pub enum $t { + $(#[$($attrss2)*] $vals = 1 << $x,)* + + /// The timer counter is reset upon external event 10. + Eevnt10 = 1 << 18, + + /// The timer counter is reset upon external event 9. + Eevnt9 = 1 << 17, + + /// The timer counter is reset upon external event 8. + Eevnt8 = 1 << 16, + + /// The timer counter is reset upon external event 7. + Eevnt7 = 1 << 15, + + /// The timer counter is reset upon external event 6. + Eevnt6 = 1 << 14, + + /// The timer counter is reset upon external event 5. + Eevnt5 = 1 << 13, + + /// The timer counter is reset upon external event 4. + Eevnt4 = 1 << 12, + + /// The timer counter is reset upon external event 3. + Eevnt3 = 1 << 11, + + /// The timer counter is reset upon external event 2. + Eevnt2 = 1 << 10, + + /// The timer counter is reset upon external event 1. + Eevnt1 = 1 << 9, + + /// The timer counter is reset upon master timer compare 4 event. + MasterCmp4 = 1 << 8, + + /// The timer counter is reset upon master timer compare 3 event. + MasterCmp3 = 1 << 7, + + /// The timer counter is reset upon master timer compare 2 event. + MasterCmp2 = 1 << 6, + + /// The timer counter is reset upon master timer compare 1 event. + MasterCmp1 = 1 << 5, + + /// The timer counter is reset upon master timer period event. + MasterPeriod = 1 << 4, + + /// The timer counter is reset upon timer its own compare 4 event + Cmp4 = 1 << 3, + + /// The timer counter is reset upon timer its own compare 2 event + Cmp2 = 1 << 2, + + /// The timer counter is reset upon update event. + Update = 1 << 1, + } + }; +} + +hr_timer_reset_event_source_common!( + /// A + pub enum TimerAResetEventSource { + [COMMON], + /// The timer counter is reset upon timer F compare 2 event. + TimFCmp2 = 1 << 31, + + /// The timer counter is reset upon timer E compare 4 event. + TimECmp4 = 1 << 30, + + /// The timer counter is reset upon timer E compare 2 event. + TimECmp2 = 1 << 29, + + /// The timer counter is reset upon timer E compare 1 event. + TimECmp1 = 1 << 28, + + /// The timer counter is reset upon timer D compare 4 event. + TimDCmp4 = 1 << 27, + + /// The timer counter is reset upon timer D compare 2 event. + TimDCmp2 = 1 << 26, + + /// The timer counter is reset upon timer D compare 1 event. + TimDCmp1 = 1 << 25, + + /// The timer counter is reset upon timer C compare 4 event. + TimCCmp4 = 1 << 24, + + /// The timer counter is reset upon timer C compare 2 event. + TimCCmp2 = 1 << 23, + + /// The timer counter is reset upon timer C compare 1 event. + TimCCmp1 = 1 << 22, + + /// The timer counter is reset upon timer B compare 4 event. + TimBCmp4 = 1 << 21, + + /// The timer counter is reset upon timer B compare 2 event. + TimBCmp2 = 1 << 20, + + /// The timer counter is reset upon timer B compare 1 event. + TimBCmp1 = 1 << 19, + + /// The timer counter is reset upon timer F compare 1 event. + TimFCmp1 = 1 << 0, + } +); + +hr_timer_reset_event_source_common!( + /// B + pub enum TimerBResetEventSource { + [COMMON], + + /// The timer counter is reset upon timer F compare 2 event. + TimFCmp2 = 1 << 31, + + /// The timer counter is reset upon timer E compare 4 event. + TimECmp4 = 1 << 30, + + /// The timer counter is reset upon timer E compare 2 event. + TimECmp2 = 1 << 29, + + /// The timer counter is reset upon timer E compare 1 event. + TimECmp1 = 1 << 28, + + /// The timer counter is reset upon timer D compare 4 event. + TimDCmp4 = 1 << 27, + + /// The timer counter is reset upon timer D compare 2 event. + TimDCmp2 = 1 << 26, + + /// The timer counter is reset upon timer D compare 1 event. + TimDCmp1 = 1 << 25, + + /// The timer counter is reset upon timer C compare 4 event. + TimCCmp4 = 1 << 24, + + /// The timer counter is reset upon timer C compare 2 event. + TimCCmp2 = 1 << 23, + + /// The timer counter is reset upon timer C compare 1 event. + TimCCmp1 = 1 << 22, + + /// The timer counter is reset upon timer A compare 4 event. + TimACmp4 = 1 << 21, + + /// The timer counter is reset upon timer A compare 2 event. + TimACmp2 = 1 << 20, + + /// The timer counter is reset upon timer A compare 1 event. + TimACmp1 = 1 << 19, + + /// The timer counter is reset upon timer F compare 1 event. + TimFCmp1 = 1 << 0, + } +); + +hr_timer_reset_event_source_common!( + /// C + pub enum TimerCResetEventSource { + [COMMON], + + /// The timer counter is reset upon timer F compare 2 event. + TimFCmp2 = 1 << 31, + + /// The timer counter is reset upon timer E compare 4 event. + TimECmp4 = 1 << 30, + + /// The timer counter is reset upon timer E compare 2 event. + TimECmp2 = 1 << 29, + + /// The timer counter is reset upon timer E compare 1 event. + TimECmp1 = 1 << 28, + + /// The timer counter is reset upon timer D compare 4 event. + TimDCmp4 = 1 << 27, + + /// The timer counter is reset upon timer D compare 2 event. + TimDCmp2 = 1 << 26, + + /// The timer counter is reset upon timer D compare 1 event. + TimDCmp1 = 1 << 25, + + /// The timer counter is reset upon timer B compare 4 event. + TimBCmp4 = 1 << 24, + + /// The timer counter is reset upon timer B compare 2 event. + TimBCmp2 = 1 << 23, + + /// The timer counter is reset upon timer B compare 1 event. + TimBCmp1 = 1 << 22, + + /// The timer counter is reset upon timer A compare 4 event. + TimACmp4 = 1 << 21, + + /// The timer counter is reset upon timer A compare 2 event. + TimACmp2 = 1 << 20, + + /// The timer counter is reset upon timer A compare 1 event. + TimACmp1 = 1 << 19, + + + /// The timer counter is reset upon timer F compare 1 event. + TimFCmp1 = 1 << 0, + } +); + +hr_timer_reset_event_source_common!( + /// D + pub enum TimerDResetEventSource { + [COMMON], + + /// The timer counter is reset upon timer F compare 2 event. + TimFCmp2 = 1 << 31, + + /// The timer counter is reset upon timer E compare 4 event. + TimECmp4 = 1 << 30, + + /// The timer counter is reset upon timer E compare 2 event. + TimECmp2 = 1 << 29, + + /// The timer counter is reset upon timer E compare 1 event. + TimECmp1 = 1 << 28, + + /// The timer counter is reset upon timer C compare 4 event. + TimCCmp4 = 1 << 27, + + /// The timer counter is reset upon timer C compare 2 event. + TimCCmp2 = 1 << 26, + + /// The timer counter is reset upon timer C compare 1 event. + TimCCmp1 = 1 << 25, + + /// The timer counter is reset upon timer B compare 4 event. + TimBCmp4 = 1 << 24, + + /// The timer counter is reset upon timer B compare 2 event. + TimBCmp2 = 1 << 23, + + /// The timer counter is reset upon timer B compare 1 event. + TimBCmp1 = 1 << 22, + + /// The timer counter is reset upon timer A compare 4 event. + TimACmp4 = 1 << 21, + + /// The timer counter is reset upon timer A compare 2 event. + TimACmp2 = 1 << 20, + + /// The timer counter is reset upon timer A compare 1 event. + TimACmp1 = 1 << 19, + + /// The timer counter is reset upon timer F compare 1 event. + TimFCmp1 = 1 << 0, + } +); + +hr_timer_reset_event_source_common!( + /// E + pub enum TimerEResetEventSource { + [COMMON], + + /// The timer counter is reset upon timer F compare 2 event. + TimFCmp2 = 1 << 31, + + /// The timer counter is reset upon timer D compare 4 event. + TimDCmp4 = 1 << 30, + + /// The timer counter is reset upon timer D compare 2 event. + TimDCmp2 = 1 << 29, + + /// The timer counter is reset upon timer D compare 1 event. + TimDCmp1 = 1 << 28, + + /// The timer counter is reset upon timer C compare 4 event. + TimCCmp4 = 1 << 27, + + /// The timer counter is reset upon timer C compare 2 event. + TimCCmp2 = 1 << 26, + + /// The timer counter is reset upon timer C compare 1 event. + TimCCmp1 = 1 << 25, + + /// The timer counter is reset upon timer B compare 4 event. + TimBCmp4 = 1 << 24, + + /// The timer counter is reset upon timer B compare 2 event. + TimBCmp2 = 1 << 23, + + /// The timer counter is reset upon timer B compare 1 event. + TimBCmp1 = 1 << 22, + + /// The timer counter is reset upon timer A compare 4 event. + TimACmp4 = 1 << 21, + + /// The timer counter is reset upon timer A compare 2 event. + TimACmp2 = 1 << 20, + + /// The timer counter is reset upon timer A compare 1 event. + TimACmp1 = 1 << 19, + + + /// The timer counter is reset upon timer F compare 1 event. + TimFCmp1 = 1 << 0, + } +); + +hr_timer_reset_event_source_common!( + /// F + pub enum TimerFResetEventSource { + [COMMON], + + /// The timer counter is reset upon timer E compare 2 event. + TimECmp2 = 1 << 31, + + /// The timer counter is reset upon timer D compare 4 event. + TimDCmp4 = 1 << 30, + + /// The timer counter is reset upon timer D compare 2 event. + TimDCmp2 = 1 << 29, + + /// The timer counter is reset upon timer D compare 1 event. + TimDCmp1 = 1 << 28, + + /// The timer counter is reset upon timer C compare 4 event. + TimCCmp4 = 1 << 27, + + /// The timer counter is reset upon timer C compare 2 event. + TimCCmp2 = 1 << 26, + + /// The timer counter is reset upon timer C compare 1 event. + TimCCmp1 = 1 << 25, + + /// The timer counter is reset upon timer B compare 4 event. + TimBCmp4 = 1 << 24, + + /// The timer counter is reset upon timer B compare 2 event. + TimBCmp2 = 1 << 23, + + /// The timer counter is reset upon timer B compare 1 event. + TimBCmp1 = 1 << 22, + + /// The timer counter is reset upon timer A compare 4 event. + TimACmp4 = 1 << 21, + + /// The timer counter is reset upon timer A compare 2 event. + TimACmp2 = 1 << 20, + + /// The timer counter is reset upon timer A compare 1 event. + TimACmp1 = 1 << 19, + + /// The timer counter is reset upon timer E compare 1 event. + TimECmp1 = 1 << 0, + } +); diff --git a/src/hrtim/external_event.rs b/src/hrtim/external_event.rs new file mode 100644 index 00000000..74b6a539 --- /dev/null +++ b/src/hrtim/external_event.rs @@ -0,0 +1,367 @@ +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; + +#[non_exhaustive] +#[derive(Copy, Clone, PartialEq)] +pub struct ExternalEventSource; + +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, + eev_input2: EevInput, + eev_input3: EevInput, + eev_input4: EevInput, + eev_input5: EevInput, + eev_input6: EevInput, + eev_input7: EevInput, + eev_input8: EevInput, + eev_input9: EevInput, + eev_input10: EevInput, + } + } +} + +#[non_exhaustive] +pub struct EevInput; + +/// 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; +} + +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 + } + } + + /// 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 + } + } + + /// 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..c425077f --- /dev/null +++ b/src/hrtim/fault.rs @@ -0,0 +1,270 @@ +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::stm32::HRTIM_COMMON; + +use super::control::HrPwmCtrl; + +/// Allows a FaultMonitor to monitor faults +pub trait FaultMonitor { + fn enable_interrupt(&mut self, hr_control: &mut HrPwmCtrl); + + /// Returns true if a fault is preventing PWM output + fn is_fault_active(&self) -> bool; + + /// Clear the fault interrupt flag + /// + /// This will *NOT* resume normal PWM operation. The affected outputs need to be re-enabled to resume operation; + /// This will do nothing if the fault is still active. + fn clear_fault(&mut self); +} + +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 + #[non_exhaustive] + pub struct $input; + + #[non_exhaustive] + #[derive(Copy, Clone)] + pub struct $source; + + 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 + } + + 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, 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, + fault_input2: FaultInput2, + fault_input3: FaultInput3, + fault_input4: FaultInput4, + fault_input5: FaultInput5, + fault_input6: FaultInput6, + } + } +} + +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, $fltxie:ident),)+) => {$( + #[non_exhaustive] + pub struct $t; + + impl FaultMonitor for $t { + fn enable_interrupt(&mut self, _hr_control: &mut HrPwmCtrl) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.ier().modify(|_r, w| w.$fltxie().set_bit()); + } + + 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().clear()); + } + } + )+}; +} + +impl_flt_monitor!( + FltMonitorSys: (sysflt, sysfltc, sysfltie), + FltMonitor1: (flt1, flt1c, flt1ie), + FltMonitor2: (flt2, flt2c, flt2ie), + FltMonitor3: (flt3, flt3c, flt3ie), + FltMonitor4: (flt4, flt4c, flt4ie), + FltMonitor5: (flt5, flt5c, flt5ie), + FltMonitor6: (flt6, flt6c, flt6ie), +); diff --git a/src/hrtim/mod.rs b/src/hrtim/mod.rs new file mode 100644 index 00000000..d07ef3c2 --- /dev/null +++ b/src/hrtim/mod.rs @@ -0,0 +1,782 @@ +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::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 capture::{HrCaptCh1, HrCaptCh2}; +use fugit::HertzU64; + +use self::control::HrPwmControl; + +use self::deadtime::DeadtimeConfig; +use self::output::ToHrOut; +use self::timer_eev_cfg::EevCfgs; + +use crate::pwm::{self, Alignment, Polarity, 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, +} + +pub trait HrPwmAdvExt: Sized { + type PreloadSource; + + fn pwm_advanced( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder + where + PINS: ToHrOut; +} + +/// HrPwmBuilder is used to configure advanced HrTim PWM features +pub struct HrPwmBuilder { + _tim: PhantomData, + _prescaler: PhantomData, + pins: PINS, + 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 struct HrParts { + pub timer: HrTim, HrCaptCh2>, + + pub cr1: HrCr1, + pub cr2: HrCr2, + pub cr3: HrCr3, + pub cr4: HrCr4, + + pub out: OUT, + pub dma_channel: timer::DmaChannel, +} + +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, $($out: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.cr().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 + .ckpsc().bits(prescaler_bits as u8) + }); + + $( + // Only available for timers with outputs(not HRTIM_MASTER) + #[allow(unused)] + let $out = (); + + tim.cr2().modify(|_r, w| + // Set counting direction + w.udm().bit($this.counting_direction == HrCountingDirection::UpDown) + ); + + tim.cr().modify(|_r, w| + // Push-Pull mode + w.pshpll().bit($this.enable_push_pull) + ); + )* + + // Write period + tim.perr().write(|w| unsafe { w.per().bits(period as u16) }); + + // Enable fault sources and lock configuration + $(unsafe { + // Only available for timers with outputs(not HRTIM_MASTER) + #[allow(unused)] + let $out = (); + + // Enable fault sources + let fault_enable_bits = $this.fault_enable_bits as u32; + tim.fltr().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.fltr().modify(|_r, w| w.fltlck().set_bit()); + + tim.outr().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.dtr().modify(|_r, w| w + .dtprsc().bits(prescaler as u8) + .dtr().bits(deadtime_rising_value) + .sdtr().bit(deadtime_rising_sign) + .dtf().bits(deadtime_falling_value) + .sdtf().bit(deadtime_falling_sign) + + // Lock configuration + .dtflk().set_bit() + .dtfslk().set_bit() + .dtrlk().set_bit() + .dtrslk().set_bit() + ); + tim.outr().modify(|_r, w| w.dten().set_bit()); + } + + // External event configs + let eev_cfg = $this.eev_cfg.clone(); + tim.eefr1().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.eefr2().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.eefr3().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); + + // Set repetition counter + unsafe { tim.repr().write(|w| w.rep().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() }); + + // Connect pins and let HRTIM take over control over them + $this.pins.connect_to_hrtim(); + + unsafe { + MaybeUninit::uninit().assume_init() + } + }}; + + (PreloadSource, $this:expr, $tim:expr) => {{ + match $this.preload_source { + Some(PreloadSource::OnCounterReset) => { + $tim.cr().modify(|_r, w| w + .trstu().set_bit() + .preen().set_bit() + ); + }, + Some(PreloadSource::OnMasterTimerUpdate) => { + $tim.cr().modify(|_r, w| w + .mstu().set_bit() + .preen().set_bit() + ); + } + Some(PreloadSource::OnRepetitionUpdate) => { + $tim.cr().modify(|_r, w| w + .trepu().set_bit() + .preen().set_bit() + ); + } + None => () + } + }}; + + (MasterPreloadSource, $this:expr, $tim:expr) => {{ + match $this.preload_source { + Some(MasterPreloadSource::OnMasterRepetitionUpdate) => { + $tim.cr().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, PINS> + where + P: HrtimPrescaler, + { + let HrPwmBuilder { + _tim, + _prescaler: _, + pins, + 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, + pins, + 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: $($out:ident)*,)+) => { + $( + impl HrPwmAdvExt for $TIMX { + type PreloadSource = PreloadSource; + + fn pwm_advanced( + self, + pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder + where + PINS: ToHrOut<$TIMX>, + { + // 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, + pins, + 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, PINS> + where + PSCL: HrtimPrescaler, + PINS: ToHrOut<$TIMX>, + { + pub fn finalize(self, _control: &mut HrPwmControl) -> HrParts<$TIMX, PSCL, PINS::Out> { + hrtim_finalize_body!( + self, PreloadSource, + $TIMX, $($out)* + ) + } + + 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 + } + )+ + }; +} + +impl HrPwmAdvExt for HRTIM_MASTER { + type PreloadSource = MasterPreloadSource; + + fn pwm_advanced( + self, + pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder + where + PINS: ToHrOut, + { + // 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, + pins, + 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 +where + PSCL: HrtimPrescaler, + PINS: ToHrOut, +{ + pub fn finalize(self, _control: &mut HrPwmControl) -> HrParts { + hrtim_finalize_body!(self, MasterPreloadSource, HRTIM_MASTER,) + } + + hrtim_common_methods!(HRTIM_MASTER, MasterPreloadSource); +} + +hrtim_hal! { + HRTIM_TIMA: out, + HRTIM_TIMB: out, + HRTIM_TIMC: out, + HRTIM_TIMD: out, + HRTIM_TIME: out, + HRTIM_TIMF: out, +} + +/// # 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..6d80b78d --- /dev/null +++ b/src/hrtim/output.rs @@ -0,0 +1,220 @@ +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::{ + self, + gpioa::{PA10, PA11, PA8, PA9}, + gpiob::{PB12, PB13, PB14, PB15}, + gpioc::{PC6, PC7, PC8, PC9}, + Alternate, AF13, AF3, + }, + pwm::{ComplementaryImpossible, Pins}, + stm32::HRTIM_COMMON, +}; + +mod sealed { + pub trait Sealed {} +} + +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, set1r, rst1r, + HRTIM_TIMA: HrOut2: ta2oen, ta2odis, ta2ods, set2r, rst2r, + + HRTIM_TIMB: HrOut1: tb1oen, tb1odis, tb1ods, set1r, rst1r, + HRTIM_TIMB: HrOut2: tb2oen, tb2odis, tb2ods, set2r, rst2r, + + HRTIM_TIMC: HrOut1: tc1oen, tc1odis, tc1ods, set1r, rst1r, + HRTIM_TIMC: HrOut2: tc2oen, tc2odis, tc2ods, set2r, rst2r, + + HRTIM_TIMD: HrOut1: td1oen, td1odis, td1ods, set1r, rst1r, + HRTIM_TIMD: HrOut2: td2oen, td2odis, td2ods, set2r, rst2r, + + HRTIM_TIME: HrOut1: te1oen, te1odis, te1ods, set1r, rst1r, + HRTIM_TIME: HrOut2: te2oen, te2odis, te2ods, set2r, rst2r, + + HRTIM_TIMF: HrOut1: tf1oen, tf1odis, tf1ods, set1r, rst1r, + HRTIM_TIMF: HrOut2: tf2oen, tf2odis, tf2ods, set2r, rst2r, +} + +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, +} + +impl State { + pub fn is_idle(self) -> bool { + matches!(self, State::Idle) + } + + pub fn is_running(self) -> bool { + matches!(self, State::Running) + } + + pub fn is_fault(self) -> bool { + matches!(self, State::Fault) + } +} + +pub trait ToHrOut: sealed::Sealed { + type Out; + + fn connect_to_hrtim(self); +} + +impl sealed::Sealed for (PA, PB) +where + PA: ToHrOut, + PB: ToHrOut, +{ +} + +impl ToHrOut for (PA, PB) +where + PA: ToHrOut, + PB: ToHrOut, +{ + type Out = (PA::Out, PB::Out); + + fn connect_to_hrtim(self) { + self.0.connect_to_hrtim(); + self.1.connect_to_hrtim(); + } +} + +pub struct HrOut1(PhantomData<(TIM, PSCL)>); +pub struct HrOut2(PhantomData<(TIM, PSCL)>); + +macro_rules! pins { + ($($TIMX:ty: CH1: $CH1:ident<$CH1_AF:ident>, CH2: $CH2:ident<$CH2_AF:ident>)+) => { + $( + impl sealed::Sealed<$TIMX> for $CH1> {} + impl sealed::Sealed<$TIMX> for $CH2> {} + + impl ToHrOut<$TIMX> for $CH1> { + type Out = HrOut1<$TIMX, PSCL>; + + fn connect_to_hrtim(self) { + let _: $CH1> = self.into_alternate(); + } + } + + impl ToHrOut<$TIMX> for $CH2> { + type Out = HrOut2<$TIMX, PSCL>; + + fn connect_to_hrtim(self) { + let _: $CH2> = self.into_alternate(); + } + } + )+ + } +} + +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 = (); +} + +impl sealed::Sealed for () {} +impl ToHrOut for () { + type Out = (); + + fn connect_to_hrtim(self) {} +} + +pub struct CH1(PhantomData); +pub struct CH2(PhantomData); diff --git a/src/hrtim/output.rs_old b/src/hrtim/output.rs_old new file mode 100644 index 00000000..0ab07d6d --- /dev/null +++ b/src/hrtim/output.rs_old @@ -0,0 +1,300 @@ +use core::marker::PhantomData; +use stm32g4::stm32g474::{ + HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, HRTIM_TIMF, +}; +use crate::hrtim::external_event_old::ExternalEventSource; + +use super::event::{EventSource, NeighborTimerEventSource}; +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_common { + ($TIMX:ident, $set_event:expr, $register:ident, $action:ident) => {{ + let tim = unsafe { &*$TIMX::ptr() }; + + match $set_event { + EventSource::Cr1 { .. } => tim.$register.modify(|_r, w| w.cmp1().$action()), + EventSource::Cr2 { .. } => tim.$register.modify(|_r, w| w.cmp2().$action()), + EventSource::Cr3 { .. } => tim.$register.modify(|_r, w| w.cmp3().$action()), + EventSource::Cr4 { .. } => tim.$register.modify(|_r, w| w.cmp4().$action()), + EventSource::Period { .. } => tim.$register.modify(|_r, w| w.per().$action()), + + EventSource::MasterCr1 { .. } => tim.$register.modify(|_r, w| w.mstcmp1().$action()), + EventSource::MasterCr2 { .. } => tim.$register.modify(|_r, w| w.mstcmp2().$action()), + EventSource::MasterCr3 { .. } => tim.$register.modify(|_r, w| w.mstcmp3().$action()), + EventSource::MasterCr4 { .. } => tim.$register.modify(|_r, w| w.mstcmp4().$action()), + EventSource::MasterPeriod { .. } => tim.$register.modify(|_r, w| w.mstper().$action()), + + EventSource::ExternalEvent(e) => match e { + ExternalEventSource::Eevnt1 { .. } => { + tim.$register.modify(|_r, w| w.extevnt1().$action()) + }, + ExternalEventSource::Eevnt2 { .. } => { + tim.$register.modify(|_r, w| w.extevnt2().$action()) + }, + ExternalEventSource::Eevnt3 { .. } => { + tim.$register.modify(|_r, w| w.extevnt3().$action()) + }, + ExternalEventSource::Eevnt4 { .. } => { + tim.$register.modify(|_r, w| w.extevnt4().$action()) + }, + ExternalEventSource::Eevnt5 { .. } => { + tim.$register.modify(|_r, w| w.extevnt5().$action()) + }, + ExternalEventSource::Eevnt6 { .. } => { + tim.$register.modify(|_r, w| w.extevnt6().$action()) + }, + ExternalEventSource::Eevnt7 { .. } => { + tim.$register.modify(|_r, w| w.extevnt7().$action()) + }, + ExternalEventSource::Eevnt8 { .. } => { + tim.$register.modify(|_r, w| w.extevnt8().$action()) + }, + ExternalEventSource::Eevnt9 { .. } => { + tim.$register.modify(|_r, w| w.extevnt9().$action()) + }, + ExternalEventSource::Eevnt10 { .. } => { + tim.$register.modify(|_r, w| w.extevnt10().$action()) + }, + } + + EventSource::NeighborTimer { n } => match n { + NeighborTimerEventSource::TimEvent1 { .. } => { + tim.$register.modify(|_r, w| w.timevnt1().$action()) + } + NeighborTimerEventSource::TimEvent2 { .. } => { + tim.$register.modify(|_r, w| w.timevnt2().$action()) + } + NeighborTimerEventSource::TimEvent3 { .. } => { + tim.$register.modify(|_r, w| w.timevnt3().$action()) + } + NeighborTimerEventSource::TimEvent4 { .. } => { + tim.$register.modify(|_r, w| w.timevnt4().$action()) + } + NeighborTimerEventSource::TimEvent5 { .. } => { + tim.$register.modify(|_r, w| w.timevnt5().$action()) + } + NeighborTimerEventSource::TimEvent6 { .. } => { + tim.$register.modify(|_r, w| w.timevnt6().$action()) + } + NeighborTimerEventSource::TimEvent7 { .. } => { + tim.$register.modify(|_r, w| w.timevnt7().$action()) + } + NeighborTimerEventSource::TimEvent8 { .. } => { + tim.$register.modify(|_r, w| w.timevnt8().$action()) + } + NeighborTimerEventSource::TimEvent9 { .. } => { + tim.$register.modify(|_r, w| w.timevnt9().$action()) + } + }, + } + }}; +} + +macro_rules! hrtim_out { + ($($TIMX:ident: $out_type:ident: $tXYoen:ident, $tXYodis:ident, $tXYods:ident, $setXYr:ident, $rstXYr:ident,)+) => {$( + impl HrOutput 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: impl Into>) { + hrtim_out_common!($TIMX, set_event.into(), $setXYr, set_bit) + } + fn disable_set_event(&mut self, set_event: impl Into>) { + hrtim_out_common!($TIMX, set_event.into(), $setXYr, clear_bit) + } + + fn enable_rst_event(&mut self, reset_event: impl Into>) { + hrtim_out_common!($TIMX, reset_event.into(), $rstXYr, set_bit) + } + fn disable_rst_event(&mut self, reset_event: impl Into>) { + hrtim_out_common!($TIMX, reset_event.into(), $rstXYr, clear_bit) + } + + 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: impl Into>); + + /// Stop listening to the specified event + fn disable_set_event(&mut self, set_event: impl Into>); + + /// 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: impl Into>); + + /// Stop listening to the specified event + fn disable_rst_event(&mut self, reset_event: impl Into>); + + /// Get current state of the output + fn get_state(&self) -> State; +} + +#[derive(Debug, PartialEq)] +#[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..bbc5dc86 --- /dev/null +++ b/src/hrtim/timer.rs @@ -0,0 +1,355 @@ +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, HrCapture}, + control::HrPwmCtrl, + 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` + /// + /// NOTE: The effective period in number of ticks will be twice as large as + /// returned by this function when running in UpDown mode or PushPull mode. + /// 4 times as large when having both modes active + 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 HrPwmCtrl); + + /// Stop timer + fn stop(&mut self, _hr_control: &mut HrPwmCtrl); + + /// Stop timer and reset counter + fn stop_and_reset(&mut self, _hr_control: &mut HrPwmCtrl); + + fn clear_repetition_interrupt(&mut self); + + /// Make a handle to this timers reset/roll-over 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; + + /// Disable register updates + /// + /// Calling this function temporarily disables the transfer from preload to active registers, + /// whatever the selected update event. This allows to modify several registers. + /// The regular update event takes place once [`Self::enable_register_updates`] is called. + fn disable_register_updates(&mut self, _hr_control: &mut HrPwmCtrl); + + /// Enable register updates + /// + /// See [`Self::disable_register_updates`]. + /// + /// NOTE: Register updates are enabled by default, no need to call this + /// unless [`Self::disable_register_updates`] has been called. + fn enable_register_updates(&mut self, _hr_control: &mut HrPwmCtrl); +} + +pub trait HrSlaveTimer: HrTimer { + type CptCh1; + type CptCh2; + + /// 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, + ); +} + +pub struct TimerSplitCapture { + pub timer: HrTim, + pub ch1: HrCapt, + pub ch2: HrCapt, +} + +/// Trait for unsplit slave timer which still contains its capture modules +pub trait HrSlaveTimerCpt: HrSlaveTimer { + type CaptureCh1: HrCapture; + type CaptureCh2: HrCapture; + + fn capture_ch1(&mut self) -> &mut ::CaptureCh1; + fn capture_ch2(&mut self) -> &mut ::CaptureCh2; + fn split_capture( + self, + ) -> TimerSplitCapture; +} + +macro_rules! hrtim_timer { + ($( + $TIMX:ident: + $tXcen:ident, + $tXudis: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.perr().read().per().bits() + } + fn set_period(&mut self, period: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.perr().write(|w| unsafe { w.per().bits(period as u16) }); + } + + /// Start timer + fn start(&mut self, _hr_control: &mut HrPwmCtrl) { + // Start timer + + // SAFETY: Since we hold _hr_control there is no risk for a race condition + let master = unsafe { &*HRTIM_MASTER::ptr() }; + master.cr().modify(|_r, w| { w.$tXcen().set_bit() }); + } + + /// Stop timer + fn stop(&mut self, _hr_control: &mut HrPwmCtrl) { + // Stop counter + // SAFETY: Since we hold _hr_control there is no risk for a race condition + let master = unsafe { &*HRTIM_MASTER::ptr() }; + master.cr().modify(|_r, w| { w.$tXcen().set_bit() }); + } + + /// Stop timer and reset counter + fn stop_and_reset(&mut self, _hr_control: &mut HrPwmCtrl) { + self.stop(_hr_control); + + // Reset counter + let tim = unsafe { &*$TIMX::ptr() }; + unsafe { tim.cntr().write(|w| w.cnt().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().clear()); + } + + /// Disable register updates + /// + /// Calling this function temporarily disables the transfer from preload to active registers, + /// whatever the selected update event. This allows to modify several registers. + /// The regular update event takes place once [`Self::enable_register_updates`] is called. + fn disable_register_updates(&mut self, _hr_control: &mut HrPwmCtrl) { + use super::HRTIM_COMMON; + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.cr1().modify(|_r, w| w.$tXudis().set_bit()); + } + + /// Enable register updates + /// + /// See [`Self::disable_register_updates`]. + /// + /// NOTE: Register updates are enabled by default, no need to call this + /// unless [`Self::disable_register_updates`] has been called. + fn enable_register_updates<'a>(&mut self, _hr_control: &mut HrPwmCtrl) { + use super::HRTIM_COMMON; + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.cr1().modify(|_r, w| w.$tXudis().clear_bit()); + } + } + + impl HrTim<$TIMX, PSCL, CPT1, CPT2> { + pub fn set_repetition_counter(&mut self, repetition_counter: u8) { + let tim = unsafe { &*$TIMX::ptr() }; + + unsafe { tim.repr().write(|w| w.rep().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 CptCh1 = HrCapt; + type CptCh2 = 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>> { + type CaptureCh1 = ::CptCh1; + type CaptureCh2 = ::CptCh2; + + /// 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) -> TimerSplitCapture<$TIMX, PSCL, capture::Ch1, capture::Ch2> { + let HrTim{ + _timer, + _prescaler, + capture_ch1, + capture_ch2, + } = self; + + TimerSplitCapture { + timer: HrTim{ + _timer, + _prescaler, + capture_ch1: (), + capture_ch2: (), + }, + ch1: capture_ch1, + ch2: capture_ch2, + } + } + } + + /// Timer Period event + impl super::event::EventSource for HrTim<$TIMX, PSCL, CPT1, CPT2> { + // $rstXr + const BITS: u32 = 1 << 2; + } + + /// Timer Update event + 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<$TIMX> { + const BITS: u32 = $adc_trigger_bits_reset; + })* + + $(impl $AdcTrigger for super::adc_trigger::TimerPeriod<$TIMX> { + 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: mcen, mudis,, + + HRTIM_TIMA: tacen, taudis, (rstr), + HRTIM_TIMB: tbcen, tbudis, (rstr), + HRTIM_TIMC: tccen, tcudis, (rstr), + HRTIM_TIMD: tdcen, tdudis, (rstr), + HRTIM_TIME: tecen, teudis, (rstr), + HRTIM_TIMF: tfcen, tfudis, (rstr), +} + +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 93c12d07..3e2382e3 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 @@ -83,6 +82,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 102e1185..e88748e3 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 a2ab3443..5e1c4393 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 d8e4defc..8bd3db1d 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -232,10 +232,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), } diff --git a/src/serial/usart.rs b/src/serial/usart.rs index 893b735d..e89a3451 100644 --- a/src/serial/usart.rs +++ b/src/serial/usart.rs @@ -1,6 +1,8 @@ +use core::convert::Infallible; use core::fmt; use core::marker::PhantomData; +use crate::dma::traits::Stream; use crate::dma::{ mux::DmaMuxResources, traits::TargetAddress, MemoryToPeripheral, PeripheralToMemory, }; @@ -84,7 +86,7 @@ pub struct Rx { pub struct Tx { pin: Pin, usart: USART, - _dma: PhantomData, + dma: Dma, } /// Serial abstraction @@ -108,7 +110,12 @@ impl TxPin for NoTx {} pub struct NoDMA; /// Type state for Tx/Rx, indicating configuration for DMA #[derive(Debug)] -pub struct DMA; +pub struct DMA { + stream: T, + memory: M, +} + +pub struct DMAOld; pub trait SerialExt { fn usart( @@ -133,9 +140,9 @@ where } } -impl fmt::Write for Tx +impl fmt::Write for Tx where - Tx: hal::serial::Write, + Tx: hal::serial::Write, { fn write_str(&mut self, s: &str) -> fmt::Result { let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); @@ -143,6 +150,10 @@ where } } +impl embedded_io::ErrorType for Tx> { + type Error = Infallible; +} + macro_rules! uart_shared { ($USARTX:ident, $dmamux_rx:ident, $dmamux_tx:ident, tx: [ $($( #[ $pmeta1:meta ] )* ($PTX:ident, $TAF:expr),)+ ], @@ -189,7 +200,7 @@ macro_rules! uart_shared { } impl Rx<$USARTX, Pin, NoDMA> { - pub fn enable_dma(self) -> Rx<$USARTX, Pin, DMA> { + pub fn enable_dma(self) -> Rx<$USARTX, Pin, DMAOld> { // NOTE(unsafe) critical section prevents races cortex_m::interrupt::free(|_| unsafe { let cr3 = &(*$USARTX::ptr()).cr3(); @@ -204,7 +215,7 @@ macro_rules! uart_shared { } } - impl Rx<$USARTX, Pin, DMA> { + impl Rx<$USARTX, Pin, DMAOld> { pub fn disable_dma(self) -> Rx<$USARTX, Pin, NoDMA> { // NOTE(unsafe) critical section prevents races interrupt::free(|_| unsafe { @@ -280,37 +291,128 @@ macro_rules! uart_shared { let usart = unsafe { &(*$USARTX::ptr()) }; usart.isr().read().txft().bit_is_set() } + + pub fn clear_transmission_complete(&mut self) { + let usart = unsafe { &(*$USARTX::ptr()) }; + usart.icr().write(|w| w.tccf().set_bit()); + } } impl Tx<$USARTX, Pin, NoDMA> { - pub fn enable_dma(self) -> Tx<$USARTX, Pin, DMA> { + pub fn enable_dma(self, mut stream: STREAM, memory: BUF) -> Tx<$USARTX, Pin, DMA> + where + STREAM: crate::dma::traits::Stream, + BUF: embedded_dma::StaticReadBuffer + { // NOTE(unsafe) critical section prevents races interrupt::free(|_| unsafe { let cr3 = &(*$USARTX::ptr()).cr3(); cr3.modify(|_, w| w.dmat().set_bit()); }); + stream.disable(); + + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + + // Set peripheral to memory mode + stream.set_direction(crate::dma::DmaDirection::MemoryToPeripheral); + + // NOTE(unsafe) We now own this buffer and we won't call any &mut + // methods on it until the end of the DMA transfer + let (buf_ptr, buf_len) = unsafe { memory.read_buffer() }; + + // Set the memory address + // + // # Safety + // + // Must be a valid memory address + unsafe { + stream.set_memory_address( + buf_ptr as u32, + ); + } + + // Set the peripheral address or the source address in memory-to-memory mode + // + // # Safety + // + // Must be a valid peripheral address or source address + let write_register_address = &unsafe { &*<$USARTX>::ptr() }.tdr() as *const _ as u32; + unsafe { stream.set_peripheral_address(write_register_address); } + + assert!( + buf_len <= 65535, + "Hardware does not support more than 65535 transfers" + ); + + // Set the DMAMUX request line + stream.set_request_line(DmaMuxResources::$dmamux_tx as u8); + + let msize = core::mem::size_of::() / 2; + + stream.clear_interrupts(); + + // NOTE(unsafe) These values are correct because of the + // invariants of TargetAddress + unsafe { + stream.set_memory_size(msize as u8); + stream.set_peripheral_size(msize as u8); + } + + let config = crate::dma::config::DmaConfig::default() + .transfer_complete_interrupt(false) + .circular_buffer(false) + .memory_increment(true) + .priority(crate::dma::config::Priority::Low); + stream.apply_config(config); + Tx { pin: self.pin, usart: self.usart, - _dma: PhantomData, + dma: DMA{ stream, memory }, } } } - impl Tx<$USARTX, Pin, DMA> { - pub fn disable_dma(self) -> Tx<$USARTX, Pin, NoDMA> { - // NOTE(unsafe) critical section prevents races - interrupt::free(|_| unsafe { - let cr3 = &(*$USARTX::ptr()).cr3(); - cr3.modify(|_, w| w.dmat().clear_bit()); - }); + impl embedded_io::Write for Tx<$USARTX, Pin, DMA> + where + T: Stream, + M: embedded_dma::StaticReadBuffer + core::ops::DerefMut, + ::Target: core::ops::IndexMut, Output = [u8]>, + { + fn write(&mut self, buf: &[u8]) -> Result { + use core::sync::atomic::fence; - Tx { - pin: self.pin, - usart: self.usart, - _dma: PhantomData, + let (_p, buffer_capacity) = unsafe { self.dma.memory.read_buffer() }; + let count = buf.len().min(buffer_capacity); + + embedded_io::Write::flush(self)?; + + fence(core::sync::atomic::Ordering::SeqCst); + self.dma.stream.disable(); + + fence(core::sync::atomic::Ordering::SeqCst); + + self.dma.memory[0..count].copy_from_slice(&buf[0..count]); + self.dma.stream.set_number_of_transfers(count as u16); + + fence(core::sync::atomic::Ordering::SeqCst); + + unsafe { + self.clear_transmission_complete(); + self.dma.stream.enable(); } + + Ok(count) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + while T::get_number_of_transfers() > 0 { + assert!(!T::get_transfer_error_flag()); + } + self.dma.stream.clear_transfer_complete_interrupt(); + assert!(!T::get_transfer_error_flag()); + Ok(()) } } @@ -388,7 +490,7 @@ macro_rules! uart_shared { } } - unsafe impl TargetAddress for Tx<$USARTX, Pin, DMA> { + unsafe impl TargetAddress for Tx<$USARTX, Pin, DMAOld> { #[inline(always)] fn address(&self) -> u32 { // unsafe: only the Tx part accesses the Tx register @@ -400,7 +502,7 @@ macro_rules! uart_shared { const REQUEST_LINE: Option = Some(DmaMuxResources::$dmamux_tx as u8); } - unsafe impl TargetAddress for Rx<$USARTX, Pin, DMA> { + unsafe impl TargetAddress for Rx<$USARTX, Pin, DMAOld> { #[inline(always)] fn address(&self) -> u32 { // unsafe: only the Rx part accesses the Rx register @@ -513,7 +615,7 @@ macro_rules! uart_lp { tx: Tx { pin: tx, usart, - _dma: PhantomData, + dma: NoDMA, }, rx: Rx { pin: rx, @@ -667,7 +769,7 @@ macro_rules! uart_full { tx: Tx { pin: tx, usart, - _dma: PhantomData, + dma: NoDMA, }, rx: Rx { pin: rx,