diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..aa0f3d8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +on: + pull_request: + +name: Continuous integration + +# Make sure CI fails on all warnings, including Clippy lints +env: + RUSTFLAGS: "-Dwarnings" + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + device: + - stm32f334 + + - stm32h742 + - stm32h743 + #- stm32h745 + - stm32h747cm7 + - stm32h750 + - stm32h753 + #- stm32h755 + #- stm32h757 + + - stm32g474 + - stm32g484 + features: + - defmt + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: thumbv7em-none-eabihf + override: true + + - name: Regular build + run: cargo check --features ${{ matrix.device }} --features ${{ matrix.features }} + - name: Build examples + run: cargo check --examples --features ${{ matrix.device }} --features ${{ matrix.features }} + - name: Clippy + run: cargo clippy --examples --features ${{ matrix.device }} --features ${{ matrix.features }} diff --git a/Cargo.toml b/Cargo.toml index e5eb5c9..35454e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,14 +3,26 @@ name = "stm32-hrtim" version = "0.1.0" edition = "2021" +[patch.crates-io] +stm32-hrtim = { path = "." } + +[patch."https://github.com/usbalbin/stm32-hrtim"] +stm32-hrtim = { path = "." } + [dependencies] -stm32f3 = { version = "0.15.1", optional = true } -stm32h7 = { version = "0.15.1", optional = true } -stm32g4 = { version = "0.19.0", package = "stm32g4-staging", optional = true } +stm32f3 = { git = "https://github.com/stm32-rs/stm32-rs-nightlies", optional = true } +stm32h7 = { git = "https://github.com/stm32-rs/stm32-rs-nightlies", features = ["critical-section"], optional = true } +stm32g4 = { version = "0.22.0", package = "stm32g4-staging", optional = true } -stm32f3xx-hal = { version = "0.10.0", optional = true } -stm32h7xx-hal = { version = "0.16.0", optional = true } -stm32g4xx-hal = { version = "0.0.1", optional = true } +defmt = { version = "0.3.10", optional = true } +fugit = "0.3.7" + +[dev-dependencies] +cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } +defmt-rtt = "0.4.0" +cortex-m-rt = "0.7.2" +panic-probe = { version = "0.3.0", features = ["print-defmt"] } +stm32g4xx-hal = { git = "https://github.com/usbalbin/stm32g4xx-hal", branch = "hrtim", features = ["defmt", "hrtim"] } [features] default = [] @@ -19,18 +31,68 @@ hrtim_v1 = [] hrtim_v1_1 = [] hrtim_v2 = [] -stm32f334x4 = ["stm32f3", "stm32f3xx-hal/stm32f334x4", "hrtim_v1"] -stm32f334x6 = ["stm32f3", "stm32f3xx-hal/stm32f334x6", "hrtim_v1"] -stm32f334x8 = ["stm32f3", "stm32f3xx-hal/stm32f334x8", "hrtim_v1"] - -stm32h742 = ["stm32h7", "stm32h7xx-hal/stm32h742", "hrtim_v1_1"] -stm32h743 = ["stm32h7", "stm32h7xx-hal/stm32h743", "hrtim_v1_1"] -#stm32h745 = ["stm32h7", "stm32h7xx-hal/stm32h745", "hrtim_v1_1"] -#stm32h747 = ["stm32h7", "stm32h7xx-hal/stm32h747", "hrtim_v1_1"] -stm32h750 = ["stm32h7", "stm32h7xx-hal/stm32h750", "hrtim_v1_1"] -stm32h753 = ["stm32h7", "stm32h7xx-hal/stm32h753", "hrtim_v1_1"] -#stm32h755 = ["stm32h7", "stm32h7xx-hal/stm32h755", "hrtim_v1_1"] -#stm32h757 = ["stm32h7", "stm32h7xx-hal/stm32h757", "hrtim_v1_1"] - -stm32g474 = ["stm32g4", "stm32g4xx-hal/stm32g474", "hrtim_v2"] -stm32g484 = ["stm32g4", "stm32g4xx-hal/stm32g484", "hrtim_v2"] \ No newline at end of file +stm32f3 = ["stm32f3/stm32f3x4"] +stm32h7 = ["dep:stm32h7"] +stm32g4 = [] + +stm32f334 = ["stm32f3/stm32f3x4", "hrtim_v1"] + +stm32h742 = ["stm32h7/stm32h742", "hrtim_v1_1"] +stm32h743 = ["stm32h7/stm32h743", "hrtim_v1_1"] +#stm32h745 = ["stm32h7/stm32h745", "hrtim_v1_1"] +stm32h747cm7 = ["stm32h7/stm32h747cm7", "hrtim_v1_1"] +stm32h750 = ["stm32h7/stm32h750", "hrtim_v1_1"] +stm32h753 = ["stm32h7/stm32h753", "hrtim_v1_1"] +#stm32h755 = ["stm32h7/stm32h755", "hrtim_v1_1"] +#stm32h757 = ["stm32h7/stm32h757", "hrtim_v1_1"] + +stm32g474 = ["stm32g4/stm32g474", "stm32g4xx-hal/stm32g474", "hrtim_v2"] +stm32g484 = ["stm32g4/stm32g484", "stm32g4xx-hal/stm32g484", "hrtim_v2"] +defmt = ["dep:defmt", "fugit/defmt"] + +# G4 + +[[example]] +name = "stm32g4-adc-trigger" +required-features = ["stm32g4"] +path = "examples/stm32g4/adc-trigger.rs" + +[[example]] +name = "stm32g4-capture" +required-features = ["stm32g4"] +path = "examples/stm32g4/capture.rs" + +[[example]] +name = "stm32g4-capture-dma" +required-features = ["stm32g4"] +path = "examples/stm32g4/capture-dma.rs" + +[[example]] +name = "stm32g4-eev-comp" +required-features = ["stm32g4"] +path = "examples/stm32g4/eev-comp.rs" + +[[example]] +name = "stm32g4-eev" +required-features = ["stm32g4"] +path = "examples/stm32g4/eev.rs" + +[[example]] +name = "stm32g4-flt-comp" +required-features = ["stm32g4"] +path = "examples/stm32g4/flt-comp.rs" + +[[example]] +name = "stm32g4-flt" +required-features = ["stm32g4"] +path = "examples/stm32g4/flt.rs" + +[[example]] +name = "stm32g4" +required-features = ["stm32g4"] +path = "examples/stm32g4/hrtim.rs" + +[[example]] +name = "stm32g4-master" +required-features = ["stm32g4"] +path = "examples/stm32g4/master.rs" \ No newline at end of file diff --git a/examples/stm32g4/adc-trigger.rs b/examples/stm32g4/adc-trigger.rs new file mode 100644 index 0000000..05505c7 --- /dev/null +++ b/examples/stm32g4/adc-trigger.rs @@ -0,0 +1,160 @@ +#![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 +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, output::HrOutput, timer::HrTimer, HrParts, HrPwmAdvExt, + Pscl4, +}; +use stm32g4xx_hal::{ + adc::{self, AdcClaim, ClockSource, Temperature, Vref}, + delay::SYSTDelayExt, + dma::{self, channel::DMAExt, config::DmaConfig, TransferExt}, + gpio::GpioExt, + hrtim::{HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, +}; + +#[entry] +fn main() -> ! { + const VREF: f32 = 3.3; + + defmt::info!("start"); + + let dp = Peripherals::take().unwrap(); + let cp = CorePeripherals::take().expect("cannot take core peripherals"); + + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + defmt::info!("rcc"); + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let dma::channel::Channels { ch1: dma1ch1, .. } = dp.DMA1.split(&rcc); + let config = DmaConfig::default() + .transfer_complete_interrupt(true) + .circular_buffer(true) + .memory_increment(true); + + defmt::info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let pa0 = gpioa.pa0.into_analog(); + + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + // . ^ ^ + // . | | + //AD samlp pa0 temp + let period = 0xFFFF; + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut cr3, + mut cr4, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + + cr1.set_duty(period / 2); + cr3.set_duty(period / 3); + cr4.set_duty((2 * u32::from(period) / 3) as u16); + + hr_control.adc_trigger1.enable_source(&cr3); + hr_control.adc_trigger1.enable_source(&cr4); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out2.enable_rst_event(&cr1); + + out1.enable_set_event(&timer); // Set high at new period + out2.enable_set_event(&timer); + + defmt::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).into(), + )); + adc.enable_temperature(&dp.ADC12_COMMON); + adc.set_continuous(adc::config::Continuous::Discontinuous); + adc.reset_sequence(); + adc.configure_channel( + &pa0, + adc::config::Sequence::One, + adc::config::SampleTime::Cycles_640_5, + ); + adc.configure_channel( + &Temperature, + adc::config::Sequence::Two, + adc::config::SampleTime::Cycles_640_5, + ); + + defmt::info!("Setup DMA"); + let first_buffer = cortex_m::singleton!(: [u16; 10] = [0; 10]).unwrap(); + + let mut transfer = dma1ch1.into_circ_peripheral_to_memory_transfer( + adc.enable_dma(adc::config::Dma::Continuous), + &mut first_buffer[..], + config, + ); + + transfer.start(|adc| adc.start_conversion()); + + out1.enable(); + out2.enable(); + + timer.start(&mut hr_control.control); + + loop { + let mut b = [0_u16; 4]; + let r = transfer.read_exact(&mut b); + + defmt::info!("read: {}", r); + assert!(r == b.len()); + + let millivolts = Vref::sample_to_millivolts((b[0] + b[2]) / 2); + defmt::info!("pa3: {}mV", millivolts); + let temp = Temperature::temperature_to_degrees_centigrade( + (b[1] + b[3]) / 2, + VREF, + adc::config::Resolution::Twelve, + ); + defmt::info!("temp: {}℃C", temp); + } +} diff --git a/examples/stm32g4/capture-dma.rs b/examples/stm32g4/capture-dma.rs new file mode 100644 index 0000000..bd55126 --- /dev/null +++ b/examples/stm32g4/capture-dma.rs @@ -0,0 +1,136 @@ +#![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 +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + capture, + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::{HrSlaveTimerCpt, HrTimer, TimerSplitCapture}, + HrParts, HrPwmAdvExt, Pscl128, +}; +use stm32g4xx_hal::{ + dma::{channel::DMAExt, config::DmaConfig, TransferExt}, + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + defmt::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... + defmt::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, + ); + + defmt::info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + // PA8 (D7 on Nucleo G474RE) + let pin_a = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // t1 t2 . + // | | . + // v v . + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Both, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + timer, + mut cr1, + out: mut out1, + dma_channel, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(period / 2); + + let TimerSplitCapture { + mut timer, + ch1: mut capture, + .. + } = timer.split_capture(); + timer.start(&mut hr_control.control); + out1.enable(); + + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + defmt::info!("Setup DMA"); + let channels = dp.DMA1.split(&rcc); + let config = DmaConfig::default() + .transfer_complete_interrupt(false) + .circular_buffer(true) + .memory_increment(true); + + let first_buffer = cortex_m::singleton!(: [u32; 16] = [0; 16]).unwrap(); + let mut transfer = channels.ch1.into_circ_peripheral_to_memory_transfer( + capture.enable_dma(dma_channel), + &mut first_buffer[..], + config, + ); + + transfer.start(|_| ()); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + let mut data = [0; 2]; + transfer.read_exact(&mut data); + let [t1, t2] = data.map(|x| capture::dma_value_to_signed(x, period)); + cr1.set_duty(duty as u16); + defmt::info!("Capture: t1: {}, t2: {}, duty: {}, ", t1, t2, old_duty); + old_duty = duty; + } + } +} diff --git a/examples/stm32g4/capture.rs b/examples/stm32g4/capture.rs new file mode 100644 index 0000000..85c1b26 --- /dev/null +++ b/examples/stm32g4/capture.rs @@ -0,0 +1,116 @@ +#![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 +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + capture::HrCapture, + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::{HrSlaveTimerCpt, HrTimer}, + HrParts, HrPwmAdvExt, Pscl128, +}; +use stm32g4xx_hal::{ + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + defmt::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... + defmt::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, + ); + + defmt::info!("Setup Gpio"); + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + // PA8 (D7 on Nucleo G474RE) + let pin_a = gpioa.pa8; + + // PB5 (D4 on Nucleo G474RE) + let input = gpiob.pb5.into_pull_down_input(); + + // ...with a prescaler of 128 this gives us a HrTimer with a tick rate of 30MHz + // With max the max period set, this would be 30MHz/2^16 ~= 458Hz... + let prescaler = Pscl128; + + // . . + // . 50% . + // ------ ------ + //out1 | | | | + // | | | | + // -------- ---------- -------- + let period = 0xFFFF; + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input6 = eev_inputs + .eev_input6 + .bind(input) + .edge_or_polarity(external_event::EdgeOrPolarity::Edge( + external_event::Edge::Falling, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(period) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + + cr1.set_duty(period / 2); + timer.start(&mut hr_control.control); + out.enable(); + + let capture = timer.capture_ch1(); + capture.enable_interrupt(true, &mut hr_control); + capture.add_event(&eev_input6); + + let mut old_duty = 0; + loop { + for duty in (u32::from(period) / 10)..(9 * u32::from(period) / 10) { + if let Some(value) = capture.get_signed(period) { + defmt::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/stm32g4/eev-comp.rs b/examples/stm32g4/eev-comp.rs new file mode 100644 index 0000000..ab196f9 --- /dev/null +++ b/examples/stm32g4/eev-comp.rs @@ -0,0 +1,136 @@ +#![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. +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::HrTimer, + timer_eev_cfg::{EevCfg, EevCfgs}, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + comparator::{self, ComparatorExt, ComparatorSplit}, + dac::{self, DacExt, DacOut}, + delay::SYSTDelayExt, + gpio::{GpioExt, SignalEdge}, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let exti = dp.EXTI; + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let gpioa = dp.GPIOA.split(&mut rcc); + + let input = gpioa.pa1.into_analog(); + let pin_a = gpioa.pa8; + + let dac1ch1 = dp.DAC1.constrain(dac::Dac1IntSig1, &mut rcc); + let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(); + + // Use dac to define the fault threshold + // 2^12 / 2 = 2^11 for about half of VCC + let limit = 1 << 11; + dac.set_value(limit); + + let (comp1, ..) = dp.COMP.split(&mut rcc); + + let comp1 = comp1.comparator( + &input, + &dac, + comparator::Config::default().hysteresis(comparator::Hysteresis::None), + //.output_inverted(), + &rcc.clocks, + ); + comp1.listen(SignalEdge::Rising, &exti); + let comp1 = comp1.enable().lock(); + + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input4 = eev_inputs + .eev_input4 + .bind(&comp1) + .edge_or_polarity(external_event::EdgeOrPolarity::Polarity( + Polarity::ActiveHigh, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + // . . * . + // . 33% . * . . . + // .-----. .--* . .-----. .----- + //out1 | | | | . | | | + // | | | * . | | | + // ------ ----------- ------------------------------ ----------- + // . . * . . . + // . . * . . . + // . . *-------------* . . + //eev . . | .| . . + // . . | .| . . + // ------------------------- .-------------------------------------- + // . . * . . . + // . . * . . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default().eev4(EevCfg::default())) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input4); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + defmt::info!("Started"); + + loop { + defmt::info!( + "Comp: {}, pending: {}", + comp1.output(), + comp1.is_pending(&exti) + ); + } +} diff --git a/examples/stm32g4/eev.rs b/examples/stm32g4/eev.rs new file mode 100644 index 0000000..0cf45e0 --- /dev/null +++ b/examples/stm32g4/eev.rs @@ -0,0 +1,104 @@ +#![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. +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + external_event::{self, ToExternalEventSource}, + output::HrOutput, + timer::HrTimer, + timer_eev_cfg::EevCfgs, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + gpio::GpioExt, + hrtim::{external_event::EevInputExt, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::Peripherals, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + let (mut hr_control, _flt_inputs, eev_inputs) = + dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + + let eev_input3 = eev_inputs + .eev_input3 + .bind(gpiob.pb7.into_pull_down_input()) + .edge_or_polarity(external_event::EdgeOrPolarity::Polarity( + Polarity::ActiveHigh, + )) + .finalize(&mut hr_control); + + let mut hr_control = hr_control.constrain(); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . * . + // . 33% . * . . . + // .-----. .--* .-----. .-----. .----- + //out1 | | | | | | | | | + // | | | * | | | | | + // ------ ----------- -------------- ----------- ----------- + // . . * . . . + // . . * . . . + // . . *--------* . . . + //eev . . | | . . . + // . . | | . . . + // ------------------------- ------------------------------------------ + // . . * . . . + // . . * . . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .eev_cfg(EevCfgs::default()) + .period(0xFFFF) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_rst_event(&eev_input3); + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + defmt::info!("Started"); + + loop { + cortex_m::asm::nop() + } +} diff --git a/examples/stm32g4/flt-comp.rs b/examples/stm32g4/flt-comp.rs new file mode 100644 index 0000000..b0287b5 --- /dev/null +++ b/examples/stm32g4/flt-comp.rs @@ -0,0 +1,140 @@ +#![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. +use cortex_m_rt::entry; +use fugit::ExtU32 as _; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, fault::{FaultAction, FaultMonitor}, output::HrOutput, timer::HrTimer, HrParts, HrPwmAdvExt, Polarity, Pscl4 +}; +use stm32g4xx_hal::{ + self as hal, adc::AdcClaim, comparator::{self, ComparatorExt, ComparatorSplit}, dac::{Dac3IntSig1, DacExt, DacOut}, delay::{DelayExt as _, SYSTDelayExt}, gpio::GpioExt, hrtim::{fault::FaultInput, HrControltExt, HrPwmBuilderExt}, pwr::PwrExt, rcc::{self, RccExt}, stm32::{CorePeripherals, Peripherals} +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 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, + comparator::Config::default() + .hysteresis(comparator::Hysteresis::None) + .output_inverted(), + &rcc.clocks, + ) + .enable(); + + let (hr_control, flt_inputs, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let fault_source5 = flt_inputs + .fault_input5 + .bind(comp3) + .polarity(Polarity::ActiveHigh) + .finalize(&mut hr_control); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + out: mut out1, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(0xFFFF) + .with_fault_source(fault_source5) // Set fault source + .fault_action1(FaultAction::ForceInactive) + .fault_action2(FaultAction::ForceInactive) + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out1.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out1.enable(); + timer.start(&mut hr_control.control); + + defmt::info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + defmt::info!( + "State: {:?}, comp: {}, is_fault_active: _, pc1: {}", + out1.get_state(), + //comp3.output(), // TODO + hr_control.fault_5.is_fault_active(), + adc1.convert(&pc1, hal::adc::config::SampleTime::Cycles_92_5) + ); + } + if hr_control.fault_5.is_fault_active() { + hr_control.fault_5.clear_fault(); // Clear fault every 5s + out1.enable(); + defmt::info!("failt cleared, and output reenabled"); + } + } +} diff --git a/examples/stm32g4/flt.rs b/examples/stm32g4/flt.rs new file mode 100644 index 0000000..3deff42 --- /dev/null +++ b/examples/stm32g4/flt.rs @@ -0,0 +1,111 @@ +#![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. +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + fault::{FaultAction, FaultMonitor}, + output::HrOutput, + timer::HrTimer, + HrParts, HrPwmAdvExt, Polarity, Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, + gpio::GpioExt, + hrtim::{fault::FaultInput, HrControltExt, HrPwmBuilderExt}, + pwr::PwrExt, + rcc::{self, RccExt}, + stm32::{CorePeripherals, Peripherals}, + time::ExtU32, +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + let (hr_control, flt_inputs, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let fault_source3 = flt_inputs + .fault_input3 + .bind(gpiob.pb10.into_pull_down_input().into_alternate()) + .polarity(Polarity::ActiveHigh) + .finalize(&mut hr_control); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1.2GHz + // With max the max period set, this would be 1.2GHz/2^16 ~= 18kHz... + let prescaler = Pscl4; + + let pin_a = gpioa.pa8; + + // . . . * + // . 33% . . * . . + // .-----. .-----. .--. . . + //out1 | | | | | | . . + // | | | | | | . . + // ------ ----------- ----------- ----------------------------------- + // . . . * . . + // . . . * . . + // . . . *-------- . . + //fault . . . | | . . + // . . . | | . . + // ----------------------------------------- -------------------------- + // . . . * . . + // . . . * . . + let HrParts { + mut timer, + mut cr1, + mut out, + .. + } = dp + .HRTIM_TIMA + .pwm_advanced(pin_a) + .prescaler(prescaler) + .period(0xFFFF) + .with_fault_source(fault_source3) + .fault_action1(FaultAction::ForceInactive) + .fault_action2(FaultAction::ForceInactive) + .finalize(&mut hr_control); + + out.enable_rst_event(&cr1); // Set low on compare match with cr1 + out.enable_set_event(&timer); // Set high at new period + cr1.set_duty(timer.get_period() / 3); + + out.enable(); + timer.start(&mut hr_control.control); + + defmt::info!("Started"); + + loop { + for _ in 0..5 { + delay.delay(500_u32.millis()); + defmt::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(); + defmt::info!("failt cleared, and output reenabled"); + } + } +} diff --git a/examples/stm32g4/hrtim.rs b/examples/stm32g4/hrtim.rs new file mode 100644 index 0000000..8187ef3 --- /dev/null +++ b/examples/stm32g4/hrtim.rs @@ -0,0 +1,96 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, output::HrOutput, timer::HrTimer, HrParts, HrPwmAdvExt, + Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, gpio::GpioExt, hrtim::{HrControltExt, HrPwmBuilderExt}, pwr::PwrExt, rcc::{self, RccExt}, stm32::{CorePeripherals, Peripherals}, time::ExtU32 +}; + +#[entry] +fn main() -> ! { + defmt::info!("Initializing..."); + + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 14.6kHz... + let prescaler = Pscl4; + + let gpioa = dp.GPIOA.split(&mut rcc); + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .period(0xFFFF) + .push_pull_mode(true) // Set push pull mode, out1 and out2 are + // alternated every period with one being + // inactive and the other getting to output its wave form + // as normal + .finalize(&mut hr_control); + + out1.enable_rst_event(&cr1); // Set low on compare match with cr1 + out2.enable_rst_event(&cr1); + + out1.enable_set_event(&timer); // Set high at new period + out2.enable_set_event(&timer); + + out1.enable(); + out2.enable(); + + loop { + // Step frequency from 14.6kHz to about 146kHz(half of that when only looking at one pin) + for i in 1..=10 { + let new_period = u16::MAX / i; + + cr1.set_duty(new_period / 3); + timer.set_period(new_period); + + delay.delay(500_u32.millis()); + } + } +} diff --git a/examples/stm32g4/master.rs b/examples/stm32g4/master.rs new file mode 100644 index 0000000..8166507 --- /dev/null +++ b/examples/stm32g4/master.rs @@ -0,0 +1,124 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use panic_probe as _; +use stm32_hrtim::{ + compare_register::HrCompareRegister, + output::HrOutput, + timer::{HrSlaveTimer, HrTimer}, + HrParts, HrPwmAdvExt, HrTimerMode, MasterPreloadSource, PreloadSource, Pscl4, +}; +use stm32g4xx_hal::{ + delay::{DelayExt, SYSTDelayExt}, gpio::GpioExt, hrtim::{HrControltExt, HrPwmBuilderExt}, pwr::PwrExt, rcc::{self, RccExt}, stm32::{CorePeripherals, Peripherals}, time::ExtU32 +}; + +#[entry] +fn main() -> ! { + let dp = Peripherals::take().expect("cannot take peripherals"); + let cp = CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 15/1/2 = 120MHz + // This would lead to HrTim running at 120MHz * 32 = 3.84... + let pwr = dp.PWR.constrain().freeze(); + let mut rcc = dp.RCC.freeze( + rcc::Config::pll().pll_cfg(rcc::PllConfig { + mux: rcc::PllSrc::HSI, + n: rcc::PllNMul::MUL_15, + m: rcc::PllMDiv::DIV_1, + r: Some(rcc::PllRDiv::DIV_2), + + ..Default::default() + }), + pwr, + ); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz + // With max the max period set, this would be 960MHz/2^16 ~= 15kHz... + let prescaler = Pscl4; + + let gpioa = dp.GPIOA.split(&mut rcc); + let pin_a = gpioa.pa8; + let pin_b = gpioa.pa9; + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (hr_control, ..) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let mut hr_control = hr_control.constrain(); + + let HrParts { + mut timer, + mut cr1, + out: (mut out1, mut out2), + .. + } = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b)) + .prescaler(prescaler) + .push_pull_mode(true) // Set push pull mode, out1 and out2 are + // alternated every period with one being + // inactive and the other getting to output its wave form + // as normal + .preload(PreloadSource::OnMasterTimerUpdate) + .timer_mode(HrTimerMode::SingleShotRetriggerable) + .finalize(&mut hr_control); + + let HrParts { + timer: mut mtimer, + cr1: mut mcr1, + .. + } = dp + .HRTIM_MASTER + .pwm_advanced(()) + .prescaler(prescaler) + .preload(MasterPreloadSource::OnMasterRepetitionUpdate) + .period(0xFFFF) + .finalize(&mut hr_control); + + // Run in sync with master timer + timer.enable_reset_event(&mtimer); + + out1.enable_rst_event(&mcr1); // Set low on compare match with cr1 + out2.enable_rst_event(&mcr1); + + out1.enable_set_event(&mtimer); // Set high at new period + out2.enable_set_event(&mtimer); + + out1.enable(); + out2.enable(); + + defmt::info!("Running"); + + loop { + // Step frequency from 15kHz to about 146kHz(half of that when only looking at one pin) + for i in 1..=10 { + let new_period = u16::MAX / i; + + mcr1.set_duty(new_period / 3); + cr1.set_duty(new_period / 3 - 1000); + mtimer.set_period(new_period); + timer.set_period(new_period - 1000); + + defmt::info!( + "period: {}, duty: {}, get_duty: {}, get_period: {}", + new_period, + new_period / 3, + mcr1.get_duty(), + mtimer.get_period() + ); + + delay.delay(500_u32.millis()); + } + } +} diff --git a/src/adc_trigger.rs b/src/adc_trigger.rs new file mode 100644 index 0000000..410e14e --- /dev/null +++ b/src/adc_trigger.rs @@ -0,0 +1,81 @@ +#[cfg(feature = "hrtim_v2")] +use crate::pac; +use core::marker::PhantomData; + +/// Handle to timers reset/roll-over event +pub struct TimerReset(pub(crate) PhantomData); + +/// Handle to timers period event +pub struct TimerPeriod(pub(crate) PhantomData); + +#[cfg(feature = "hrtim_v2")] +macro_rules! impl_adc1234_trigger { + ($($t:ident: [$trait_:ident, $adcXr:ident]),*) => {$( + #[non_exhaustive] + pub struct $t; + + impl $t { + pub fn enable_source(&mut self, _trigger: &T) { + let common = unsafe { &*pac::HRTIM_COMMON::ptr() }; + unsafe { + common.$adcXr().modify(|r, w| w.bits(r.bits() | T::BITS)); + } + } + } + )*} +} + +#[cfg(feature = "hrtim_v2")] +macro_rules! impl_adc5678910_trigger { + ($($t:ident: [$trait_:ident, $adcXtrg:ident]),*) => {$( + #[non_exhaustive] + pub struct $t; + + impl $t { + pub fn enable_source(&mut self, _trigger: &T) { + let common = unsafe { &*pac::HRTIM_COMMON::ptr() }; + common + .adcer() + .modify(|_r, w| unsafe { w.$adcXtrg().bits(T::BITS as u8) }); + } + } + )*} +} + +#[cfg(feature = "hrtim_v2")] +pub trait AdcTrigger13 { + const BITS: u32; +} + +#[cfg(feature = "hrtim_v2")] +pub trait AdcTrigger24 { + const BITS: u32; +} + +#[cfg(feature = "hrtim_v2")] +pub trait AdcTrigger579 { + const BITS: u32; +} + +#[cfg(feature = "hrtim_v2")] +pub trait AdcTrigger6810 { + const BITS: u32; +} + +#[cfg(feature = "hrtim_v2")] +impl_adc1234_trigger! { + AdcTrigger1: [AdcTrigger13, adc1r], + AdcTrigger2: [AdcTrigger24, adc2r], + AdcTrigger3: [AdcTrigger13, adc3r], + AdcTrigger4: [AdcTrigger24, adc4r] +} + +#[cfg(feature = "hrtim_v2")] +impl_adc5678910_trigger! { + AdcTrigger5: [AdcTrigger579, adc5trg], + AdcTrigger6: [AdcTrigger6810, adc6trg], + AdcTrigger7: [AdcTrigger579, adc7trg], + AdcTrigger8: [AdcTrigger6810, adc8trg], + AdcTrigger9: [AdcTrigger579, adc9trg], + AdcTrigger10: [AdcTrigger6810, adc10trg] +} diff --git a/src/capture.rs b/src/capture.rs new file mode 100644 index 0000000..1e8e469 --- /dev/null +++ b/src/capture.rs @@ -0,0 +1,268 @@ +use super::timer; + +#[cfg(feature = "hrtim_v2")] +use crate::pac::HRTIM_TIMF; +use crate::pac::{HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME}; +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, + #[cfg(feature = "hrtim_v2")] + 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) + /// + /// ```text + /// 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) + /// + /// ```text + /// Counter + /// ---------------------------------- <--- period + /// \ ^ / + /// \ | / + /// \ | / + /// \ | / + /// Down count \ | / Up count + /// \|/ + /// <-------------- 0 --------------> t + /// Negative result | positive result + /// ``` + fn get_last_signed(&self, #[allow(unused_variables)] 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), + #[cfg(feature = "hrtim_v2")] + 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; + #[cfg(feature = "hrtim_v2")] + match x & (1 << 16) != 0 { + true => (value, CountingDirection::Down), + false => (value, CountingDirection::Up), + } + + #[cfg(any(feature = "hrtim_v1", feature = "hrtim_v1_1"))] + (value, CountingDirection::Up) +} + +pub fn dma_value_to_signed(x: u32, #[allow(unused_variables)] 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), + #[cfg(feature = "hrtim_v2")] + 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(); + + #[cfg(feature = "hrtim_v2")] + let dir = match data.dir().bit() { + true => CountingDirection::Down, + false => CountingDirection::Up, + }; + #[cfg(any(feature = "hrtim_v1", feature = "hrtim_v1_1"))] + let dir = 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() + } + } + }; +} + +impl_capture! { + HRTIM_TIMA, + HRTIM_TIMB, + HRTIM_TIMC, + HRTIM_TIMD, + HRTIM_TIME +} + +#[cfg(feature = "hrtim_v2")] +impl_capture! { + HRTIM_TIMF +} diff --git a/src/compare_register.rs b/src/compare_register.rs new file mode 100644 index 0000000..da938e1 --- /dev/null +++ b/src/compare_register.rs @@ -0,0 +1,237 @@ +use core::marker::PhantomData; + +#[cfg(feature = "hrtim_v2")] +use crate::pac::HRTIM_TIMF; +use crate::pac::{HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME}; + +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)>); + +#[cfg(feature = "stm32g4")] +use super::adc_trigger::{ + AdcTrigger13 as Adc13, AdcTrigger24 as Adc24, AdcTrigger579 as Adc579, + AdcTrigger6810 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' +#[cfg(feature = "stm32g4")] +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) ] + ] +} + +// TODO: Populate more things +#[cfg(any(feature = "stm32f3", feature = "stm32h7"))] +hrtim_cr! { + HRTIM_MASTER: [ + [], [], + [], [], + [], [], + [], [] + ], + + HRTIM_TIMA: [ + [], [], + [], [], + [], [], + [], [] + ], + + HRTIM_TIMB: [ + [], [], + [], [], + [], [], + [], [] + ], + + HRTIM_TIMC: [ + [], [], + [], [], + [], [], + [], [] + ], + + HRTIM_TIMD: [ + [], [], + [], [], + [], [], + [], [] + ], + + HRTIM_TIME: [ + [], [], + [], [], + [], [], + [], [] + ] +} + +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 +} + +#[cfg(feature = "hrtim_v2")] +hrtim_timer_rst! { + HRTIM_TIMF: HrCr2: 2, + HRTIM_TIMF: HrCr4: 3 +} diff --git a/src/control.rs b/src/control.rs new file mode 100644 index 0000000..d33011a --- /dev/null +++ b/src/control.rs @@ -0,0 +1,365 @@ +#[cfg(feature = "hrtim_v2")] +use crate::adc_trigger; +#[cfg(feature = "hrtim_v2")] +use crate::fault::FltMonitor6; +use crate::fault::{ + FltMonitor1, FltMonitor2, FltMonitor3, FltMonitor4, FltMonitor5, FltMonitorSys, +}; + +use crate::pac::HRTIM_COMMON; + +use super::{external_event::EevInputs, fault::FaultInputs}; + +impl HrTimOngoingCalibration { + /// Look in the hal for an corresponding extension trait for `HRTIM_COMMON`. + /// + /// ..unless you are the one implementing the hal + /// + /// # Safety + /// The user is expected to have setup and enabled rcc clock to the peripheral + pub unsafe fn hr_control() -> HrTimOngoingCalibration { + #[allow(unused_variables)] + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + // Start calibration procedure + #[cfg(not(feature = "stm32h7"))] + common + .dllcr() + .write(|w| w.cal().set_bit().calen().clear_bit()); + + HrTimOngoingCalibration { + #[cfg(feature = "stm32g4")] + adc_trigger1_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger2_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger3_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger4_postscaler: AdcTriggerPostscaler::None, + + #[cfg(feature = "stm32g4")] + adc_trigger5_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger6_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger7_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger8_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger9_postscaler: AdcTriggerPostscaler::None, + #[cfg(feature = "stm32g4")] + adc_trigger10_postscaler: AdcTriggerPostscaler::None, + + flt_divider: SamplingClkDiv::None, + eev_divider: SamplingClkDiv::None, + } + } +} + +pub struct HrTimOngoingCalibration { + #[cfg(feature = "stm32g4")] + adc_trigger1_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger2_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger3_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger4_postscaler: AdcTriggerPostscaler, + + #[cfg(feature = "stm32g4")] + adc_trigger5_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger6_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger7_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger8_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + adc_trigger9_postscaler: AdcTriggerPostscaler, + #[cfg(feature = "stm32g4")] + 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 { + #[cfg(feature = "stm32g4")] + adc_trigger1_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger2_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger3_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger4_postscaler, + + #[cfg(feature = "stm32g4")] + adc_trigger5_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger6_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger7_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger8_postscaler, + #[cfg(feature = "stm32g4")] + adc_trigger9_postscaler, + #[cfg(feature = "stm32g4")] + 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 + #[cfg(not(feature = "stm32h7"))] + 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)); + + #[cfg(feature = "stm32g4")] + 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) + }); + + #[cfg(feature = "stm32g4")] + 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) { + #[cfg(not(feature = "stm32h7"))] + { + 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() + }) + } + + #[cfg(feature = "stm32g4")] + pub fn set_adc1_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger1_postscaler = post_scaler; + self + } + + #[cfg(feature = "stm32g4")] + pub fn set_adc2_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger2_postscaler = post_scaler; + self + } + + #[cfg(feature = "stm32g4")] + pub fn set_adc3_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self { + self.adc_trigger3_postscaler = post_scaler; + self + } + + #[cfg(feature = "stm32g4")] + 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, + #[cfg(feature = "hrtim_v2")] + fault_6: FltMonitor6, + + #[cfg(feature = "stm32g4")] + adc_trigger1: adc_trigger::AdcTrigger1, + #[cfg(feature = "stm32g4")] + adc_trigger2: adc_trigger::AdcTrigger2, + #[cfg(feature = "stm32g4")] + adc_trigger3: adc_trigger::AdcTrigger3, + #[cfg(feature = "stm32g4")] + adc_trigger4: adc_trigger::AdcTrigger4, + #[cfg(feature = "stm32g4")] + adc_trigger5: adc_trigger::AdcTrigger5, + #[cfg(feature = "stm32g4")] + adc_trigger6: adc_trigger::AdcTrigger6, + #[cfg(feature = "stm32g4")] + adc_trigger7: adc_trigger::AdcTrigger7, + #[cfg(feature = "stm32g4")] + adc_trigger8: adc_trigger::AdcTrigger8, + #[cfg(feature = "stm32g4")] + adc_trigger9: adc_trigger::AdcTrigger9, + #[cfg(feature = "stm32g4")] + adc_trigger10: adc_trigger::AdcTrigger10, + } + } +} + +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, + #[cfg(feature = "stm32g4")] + pub fault_6: FltMonitor6, + + #[cfg(feature = "stm32g4")] + pub adc_trigger1: adc_trigger::AdcTrigger1, + #[cfg(feature = "stm32g4")] + pub adc_trigger2: adc_trigger::AdcTrigger2, + #[cfg(feature = "stm32g4")] + pub adc_trigger3: adc_trigger::AdcTrigger3, + #[cfg(feature = "stm32g4")] + pub adc_trigger4: adc_trigger::AdcTrigger4, + + #[cfg(feature = "stm32g4")] + pub adc_trigger5: adc_trigger::AdcTrigger5, + #[cfg(feature = "stm32g4")] + pub adc_trigger6: adc_trigger::AdcTrigger6, + #[cfg(feature = "stm32g4")] + pub adc_trigger7: adc_trigger::AdcTrigger7, + #[cfg(feature = "stm32g4")] + pub adc_trigger8: adc_trigger::AdcTrigger8, + #[cfg(feature = "stm32g4")] + pub adc_trigger9: adc_trigger::AdcTrigger9, + #[cfg(feature = "stm32g4")] + pub adc_trigger10: adc_trigger::AdcTrigger10, +} + +#[cfg(feature = "stm32g4")] +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/deadtime.rs b/src/deadtime.rs new file mode 100644 index 0000000..c59c064 --- /dev/null +++ b/src/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/event.rs b/src/event.rs new file mode 100644 index 0000000..29a1f7b --- /dev/null +++ b/src/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/external_event.rs b/src/external_event.rs new file mode 100644 index 0000000..da63f52 --- /dev/null +++ b/src/external_event.rs @@ -0,0 +1,320 @@ +use crate::pac::HRTIM_COMMON; +use crate::Polarity; + +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) {} +} + +#[derive()] +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, +} + +#[cfg(feature = "stm32g4")] +impl SourceBuilder { + /// # Safety + /// Caller needs to ensure that src_bits is a valid bit pattern + /// for eeXsrc bits in eecr1/2 registers for the intended input + pub 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() }; + + 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/fault.rs b/src/fault.rs new file mode 100644 index 0000000..32abc24 --- /dev/null +++ b/src/fault.rs @@ -0,0 +1,264 @@ +#[cfg(feature = "hrtim_v2")] +use crate::control::HrPwmControl; +use crate::pac::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; +} + +#[cfg(feature = "stm32g4")] +pub struct SourceBuilder { + _input: I, + src_bits: u8, + + /// FLTxP + is_active_high: bool, + + /// FLTxF[3:0] + filter_bits: u8, +} + +#[cfg(feature = "stm32g4")] +impl SourceBuilder { + /// # Safety + /// Caller needs to ensure that src_bits is a valid bit pattern + /// for fltXsrc bits in fltinr1/2 registers for the intended input + pub unsafe fn new(input: I, src_bits: u8) -> Self { + SourceBuilder { + _input: input, + src_bits, + is_active_high: false, + filter_bits: 0b0000, + } + } +} + +#[cfg(feature = "hrtim_v2")] +macro_rules! impl_faults { + ($( + $input:ident => $source: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 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 = matches!(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; + } + )+} +} + +#[cfg(feature = "hrtim_v2")] +impl_faults!( + FaultInput1 => FaultSource1: 0b000001, fltinr1, flt1src, flt1src_1, flt1p, flt1f, flt1e, flt1lck, + FaultInput2 => FaultSource2: 0b000010, fltinr1, flt2src, flt2src_1, flt2p, flt2f, flt2e, flt2lck, + FaultInput3 => FaultSource3: 0b000100, fltinr1, flt3src, flt3src_1, flt3p, flt3f, flt3e, flt3lck, + FaultInput4 => FaultSource4: 0b001000, fltinr1, flt4src, flt4src_1, flt4p, flt4f, flt4e, flt4lck, + FaultInput5 => FaultSource5: 0b010000, fltinr2, flt5src, flt5src_1, flt5p, flt5f, flt5e, flt5lck, + FaultInput6 => FaultSource6: 0b100000, fltinr2, flt6src, flt6src_1, flt6p, flt6f, flt6e, flt6lck, +); + +pub struct FaultInputs { + #[cfg(feature = "hrtim_v2")] + pub fault_input1: FaultInput1, + #[cfg(feature = "hrtim_v2")] + pub fault_input2: FaultInput2, + #[cfg(feature = "hrtim_v2")] + pub fault_input3: FaultInput3, + #[cfg(feature = "hrtim_v2")] + pub fault_input4: FaultInput4, + #[cfg(feature = "hrtim_v2")] + pub fault_input5: FaultInput5, + #[cfg(feature = "hrtim_v2")] + pub fault_input6: FaultInput6, +} + +impl FaultInputs { + pub(crate) unsafe fn new() -> Self { + FaultInputs { + #[cfg(feature = "hrtim_v2")] + fault_input1: FaultInput1, + #[cfg(feature = "hrtim_v2")] + fault_input2: FaultInput2, + #[cfg(feature = "hrtim_v2")] + fault_input3: FaultInput3, + #[cfg(feature = "hrtim_v2")] + fault_input4: FaultInput4, + #[cfg(feature = "hrtim_v2")] + fault_input5: FaultInput5, + #[cfg(feature = "hrtim_v2")] + 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), +); + +#[cfg(feature = "hrtim_v2")] +impl_flt_monitor!( + FltMonitor6: (flt6, flt6c, flt6ie), +); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..31d92ac --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,849 @@ +#![no_std] + +#[cfg(not(any( + feature = "stm32f334", + feature = "stm32h742", + feature = "stm32h743", + //feature = "stm32h745", + feature = "stm32h747cm7", + feature = "stm32h750", + feature = "stm32h753", + //feature = "stm32h755", + //feature = "stm32h757", + feature = "stm32g474", + feature = "stm32g484", +)))] +compile_error!( + "This crate requires one of the following features enabled: + stm32f334 + + stm32h742 + stm32h743 + #stm32h745 + stm32h747cm7 + stm32h750 + stm32h753 + #stm32h755 + #stm32h757 + + stm32g474 + stm32g484" +); + +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; + +#[cfg(feature = "stm32f334")] +pub use stm32f3::stm32f3x4 as pac; + +#[cfg(feature = "stm32h742")] +pub use stm32h7::stm32h742 as pac; + +#[cfg(feature = "stm32h743")] +pub use stm32h7::stm32h743 as pac; + +//#[cfg(feature = "stm32h745")] +//pub use stm32h7::stm32h745 as pac; + +#[cfg(feature = "stm32h747cm7")] +pub use stm32h7::stm32h747cm7 as pac; + +#[cfg(feature = "stm32h750")] +pub use stm32h7::stm32h750 as pac; + +#[cfg(feature = "stm32h753")] +pub use stm32h7::stm32h753 as pac; + +//#[cfg(feature = "stm32h755")] +//pub use stm32h7::stm32h755 as pac; + +//#[cfg(feature = "stm32h757")] +//pub use stm32h7::stm32h757 as pac; + +#[cfg(feature = "stm32g474")] +pub use stm32g4::stm32g474 as pac; + +#[cfg(feature = "stm32g484")] +pub use stm32g4::stm32g484 as pac; + +use core::marker::PhantomData; +use core::mem::MaybeUninit; + +use crate::compare_register::{HrCr1, HrCr2, HrCr3, HrCr4}; +use crate::fault::{FaultAction, FaultSource}; +use crate::timer::HrTim; +#[cfg(feature = "hrtim_v2")] +use pac::HRTIM_TIMF; +use pac::{HRTIM_COMMON, HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME}; + +use capture::{HrCaptCh1, HrCaptCh2}; + +use self::control::HrPwmControl; + +use self::deadtime::DeadtimeConfig; +use self::output::ToHrOut; +use self::timer_eev_cfg::EevCfgs; + +/// 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, + + #[cfg(feature = "hrtim_v2")] + /// 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, +} + +#[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, + + #[cfg(feature = "hrtim_v2")] + /// 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, + + #[cfg(feature = "hrtim_v2")] + /// 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, + ) -> HrPwmBuilder + where + PINS: ToHrOut; +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Polarity { + ActiveHigh, + ActiveLow, +} + +/// HrPwmBuilder is used to configure advanced HrTim PWM features +pub struct HrPwmBuilder { + _tim: PhantomData, + _prescaler: PhantomData, + pub 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), + }; + + let (half, _intlvd) = match $this.interleaved_mode { + InterleavedMode::Disabled => (false, 0b00), + InterleavedMode::Dual => (true, 0b00), + #[cfg(feature = "hrtim_v2")] + InterleavedMode::Triple => (false, 0b01), + #[cfg(feature = "hrtim_v2")] + 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 + + // half/double interleaved mode + .half().bit(half) + + // Set prescaler + .ckpsc().bits(prescaler_bits as u8) + }); + + #[cfg(feature = "hrtim_v2")] + tim.cr().modify(|_r, w| unsafe { + // Interleaved mode + w.intlvd().bits(_intlvd) + }); + + $( + // Only available for timers with outputs(not HRTIM_MASTER) + #[allow(unused)] + let $out = (); + + #[cfg(feature = "hrtim_v2")] + 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) + ); + #[cfg(feature = "hrtim_v2")] + tim.fltr().modify(|_, w| w.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(matches!($this.out1_polarity, Polarity::ActiveLow)) + .pol2().bit(matches!($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) + ); + #[cfg(feature = "hrtim_v2")] + 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() }); + }}; + + (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 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::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, + ) -> 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>, + { + // For HAL writers: + // Make sure to connect gpios after calling this function and then it should be safe to + // conjure an instance of HrParts<$TIMX, PSCL, PINS::Out> + pub fn _init(self, _control: &mut HrPwmControl) -> PINS { + hrtim_finalize_body!(self, PreloadSource, $TIMX, [$($out)*]); + self.pins + } + + 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, + ) -> 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, []); + + unsafe { MaybeUninit::uninit().assume_init() } + } + + hrtim_common_methods!(HRTIM_MASTER, MasterPreloadSource); +} + +hrtim_hal! { + HRTIM_TIMA: out, + HRTIM_TIMB: out, + HRTIM_TIMC: out, + HRTIM_TIMD: out, + HRTIM_TIME: out, +} + +#[cfg(feature = "hrtim_v2")] +hrtim_hal! { + 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; + } + )+}; +} + +#[cfg(any(feature = "stm32f3", feature = "stm32g4"))] +pub type PsclDefault = Pscl128; + +#[cfg(feature = "stm32h7")] +pub type PsclDefault = Pscl4; + +#[cfg(any(feature = "stm32f3", feature = "stm32g4"))] +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 +} + +#[cfg(feature = "stm32h7")] +impl_pscl! { + Pscl1 => 0b101, 1, 0x0003, 0xFFFD + Pscl2 => 0b110, 2, 0x0003, 0xFFFD + Pscl4 => 0b111, 4, 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/output.rs b/src/output.rs new file mode 100644 index 0000000..f6f999e --- /dev/null +++ b/src/output.rs @@ -0,0 +1,157 @@ +#[cfg(feature = "hrtim_v2")] +use crate::pac::HRTIM_TIMF; +use crate::pac::{HRTIM_COMMON, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME}; +use core::marker::PhantomData; + +use super::event::EventSource; + +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, +} + +#[cfg(feature = "hrtim_v2")] +hrtim_out! { + 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) + } +} + +/// # Safety +/// Caller needs to ensure that this is only implemented +/// for types that represent pin that can act as an output +/// for the specified timer `TIM` +pub unsafe trait ToHrOut { + type Out; +} + +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)>); + +unsafe impl ToHrOut for () { + type Out = (); +} + +pub struct CH1(PhantomData); +pub struct CH2(PhantomData); diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..182b97c --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,359 @@ +#[cfg(feature = "hrtim_v2")] +use crate::pac::HRTIM_TIMF; +use crate::pac::{HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME}; +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; + } + )* + )+} +} + +#[cfg(feature = "stm32g4")] +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; + })* + )*)* + } +} + +#[cfg(feature = "stm32g4")] +use super::adc_trigger::{ + AdcTrigger13 as Adc13, AdcTrigger24 as Adc24, AdcTrigger579 as Adc579, + AdcTrigger6810 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), +} +#[cfg(feature = "hrtim_v2")] +hrtim_timer! {HRTIM_TIMF: tfcen, tfudis, (rstr),} + +#[cfg(feature = "stm32g4")] +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/timer_eev_cfg.rs b/src/timer_eev_cfg.rs new file mode 100644 index 0000000..545a84a --- /dev/null +++ b/src/timer_eev_cfg.rs @@ -0,0 +1,206 @@ +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. + #[cfg(feature = "hrtim_v2")] + pub(crate) event_counter_enable_bit: bool, + #[cfg(feature = "hrtim_v2")] + pub(crate) event_counter_reset_mode_bit: bool, + #[cfg(feature = "hrtim_v2")] + pub(crate) event_counter_source_bits: u8, + #[cfg(feature = "hrtim_v2")] + 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(), + #[cfg(feature = "hrtim_v2")] + event_counter_enable_bit: self.event_counter_enable_bit, + #[cfg(feature = "hrtim_v2")] + event_counter_reset_mode_bit: self.event_counter_reset_mode_bit, + #[cfg(feature = "hrtim_v2")] + event_counter_source_bits: self.event_counter_source_bits, + #[cfg(feature = "hrtim_v2")] + 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(), + #[cfg(feature = "hrtim_v2")] + event_counter_enable_bit: false, + #[cfg(feature = "hrtim_v2")] + event_counter_reset_mode_bit: false, + #[cfg(feature = "hrtim_v2")] + event_counter_source_bits: 0, + #[cfg(feature = "hrtim_v2")] + event_counter_threshold_bits: 0, + } + } +}