diff --git a/Cargo.toml b/Cargo.toml index e20668ca..df2af6da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ paste = "1.0" bitflags = "1.2" vcell = "0.1" static_assertions = "1.1" +fugit = "0.3.5" [dependencies.cortex-m] version = "0.7.7" diff --git a/examples/blinky_delay.rs b/examples/blinky_delay.rs index 38b8c790..cac9a15f 100644 --- a/examples/blinky_delay.rs +++ b/examples/blinky_delay.rs @@ -7,6 +7,7 @@ use hal::delay::DelayFromCountDownTimer; use hal::prelude::*; use hal::rcc::Config; use hal::stm32; +use hal::time::ExtU32; use hal::timer::Timer; use stm32g4xx_hal as hal; @@ -34,13 +35,13 @@ fn main() -> ! { info!("Init Timer2 delay"); let timer2 = Timer::new(dp.TIM2, &rcc.clocks); - let mut delay_tim2 = DelayFromCountDownTimer::new(timer2.start_count_down(100.ms())); + let mut delay_tim2 = DelayFromCountDownTimer::new(timer2.start_count_down(100.millis())); loop { info!("Toggle"); led.toggle().unwrap(); info!("SYST delay"); - delay_syst.delay(1000.ms()); + delay_syst.delay(1000.millis()); info!("Toggle"); led.toggle().unwrap(); info!("TIM2 delay"); diff --git a/examples/can-echo.rs b/examples/can-echo.rs index d458a3ed..89911b8e 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -7,7 +7,7 @@ use crate::hal::{ nb::block, rcc::{Config, RccExt, SysClockSrc}, stm32::Peripherals, - time::U32Ext, + time::RateExtU32, }; use fdcan::{ config::NominalBitTiming, @@ -47,7 +47,7 @@ fn main() -> ! { let dp = Peripherals::take().unwrap(); let _cp = cortex_m::Peripherals::take().expect("cannot take core peripherals"); let rcc = dp.RCC.constrain(); - let mut rcc = rcc.freeze(Config::new(SysClockSrc::HSE(24.mhz()))); + let mut rcc = rcc.freeze(Config::new(SysClockSrc::HSE(24.MHz()))); info!("Split GPIO"); diff --git a/examples/hrtim.rs b/examples/hrtim.rs new file mode 100644 index 00000000..8043f448 --- /dev/null +++ b/examples/hrtim.rs @@ -0,0 +1,104 @@ +//This example puts the timer in PWM mode using the specified pin with a frequency of 100Hz and a duty cycle of 50%. +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +#[cfg(not(any(feature = "stm32g474", feature = "stm32g484")))] +#[entry] +fn main() -> ! { + #[allow(clippy::empty_loop)] + loop {} +} + +#[cfg(any(feature = "stm32g474", feature = "stm32g484"))] +#[entry] +fn main() -> ! { + use fugit::ExtU32; + use hal::gpio::gpioa::PA8; + use hal::gpio::gpioa::PA9; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::prelude::*; + use hal::pwm::hrtim::EventSource; + use hal::pwm::hrtim::HrCompareRegister; + use hal::pwm::hrtim::HrControltExt; + use hal::pwm::hrtim::HrOutput; + use hal::pwm::hrtim::HrPwmAdvExt; + use hal::pwm::hrtim::HrTimer; + use hal::pwm::hrtim::Pscl4; + use hal::rcc; + use hal::stm32; + use stm32g4xx_hal as hal; + extern crate cortex_m_rt as rt; + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let cp = stm32::CorePeripherals::take().expect("cannot take core"); + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + 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() + })); + + let mut delay = cp.SYST.delay(&rcc.clocks); + + // ...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 gpioa = dp.GPIOA.split(&mut rcc); + let pin_a: PA8<Alternate<AF13>> = gpioa.pa8.into_alternate(); + let pin_b: PA9<Alternate<AF13>> = gpioa.pa9.into_alternate(); + + // . . . . + // . 30% . . . + // ---- . .---- . + //out1 | | . | | . + // | | . | | . + // -------- ---------------------------- -------------------- + // . .---- . .---- + //out2 . | | . | | + // . | | . | | + // ------------------------ ---------------------------- ---- + // . . . . + // . . . . + let (mut fault_control, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let (mut timer, (mut cr1, _cr2, _cr3, _cr4), (mut out1, mut out2)) = dp + .HRTIM_TIMA + .pwm_advanced((pin_a, pin_b), &mut rcc) + .prescaler(prescaler) + .period(0xFFFF) + .push_pull_mode(true) // Set push pull mode, out1 and out2 are + // alternated every period with one being + // inactive and the other getting to output its wave form + // as normal + .finalize(&mut fault_control); + + out1.enable_rst_event(EventSource::Cr1); // Set low on compare match with cr1 + out2.enable_rst_event(EventSource::Cr1); + + out1.enable_set_event(EventSource::Period); // Set high at new period + out2.enable_set_event(EventSource::Period); + + out1.enable(); + out2.enable(); + + loop { + // Step frequency from 18kHz to about 180kHz(half of that when only looking at one pin) + for i in 1..10 { + let new_period = u16::MAX / i; + + cr1.set_duty(new_period / 3); + timer.set_period(new_period); + + delay.delay(500_u32.millis()); + } + } +} diff --git a/examples/hrtim_simple.rs b/examples/hrtim_simple.rs new file mode 100644 index 00000000..df142cd2 --- /dev/null +++ b/examples/hrtim_simple.rs @@ -0,0 +1,79 @@ +//This example puts the timer in PWM mode using the specified pin with a frequency of 100Hz and a duty cycle of 50%. +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +//mod utils; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +#[cfg(not(any(feature = "stm32g474", feature = "stm32g484")))] +#[entry] +fn main() -> ! { + #[allow(clippy::empty_loop)] + loop {} +} + +#[cfg(any(feature = "stm32g474", feature = "stm32g484"))] +#[entry] +fn main() -> ! { + use hal::gpio::gpioa::PA8; + use hal::gpio::gpioa::PA9; + use hal::gpio::Alternate; + use hal::gpio::AF13; + use hal::prelude::*; + use hal::pwm::hrtim::{HrControltExt, HrPwmExt}; + use hal::rcc; + use hal::stm32; + use hal::time::RateExtU32; + use stm32g4xx_hal as hal; + extern crate cortex_m_rt as rt; + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + + // Set system frequency to 16MHz * 75/4/2 = 150MHz + // This would lead to HrTim running at 150MHz * 32 = 4.8GHz... + let mut rcc = dp.RCC.freeze(rcc::Config::pll().pll_cfg(rcc::PllConfig { + n: rcc::PllNMul::MUL_75, + m: rcc::PllMDiv::DIV_4, + r: Some(rcc::PllRDiv::DIV_2), + ..Default::default() + })); + + let gpioa = dp.GPIOA.split(&mut rcc); + let pin_a: PA8<Alternate<AF13>> = gpioa.pa8.into_alternate(); + let pin_b: PA9<Alternate<AF13>> = gpioa.pa9.into_alternate(); + + // . . . + // . 33% . . + // ---- .---- .---- + //out1 | | | | | | + // | | | | | | + // ------ ------------ ------------ --------- + // . . + // 50% . . + // -------- .-------- .-------- + //out2 | | | | | | + // | | | | | | + // ------ -------- -------- ----- + // . . . + // . . . + + let (mut control, _) = dp.HRTIM_COMMON.hr_control(&mut rcc).wait_for_calibration(); + let (mut p1, mut p2) = dp + .HRTIM_TIMA + .pwm((pin_a, pin_b), 20_u32.kHz(), &mut control, &mut rcc); + let max_duty = p1.get_max_duty(); + + p1.set_duty(max_duty / 3); // Set output 1 to about 33% + p2.set_duty(max_duty / 2); // Set output 2 to 50% + + // Enable the outputs + p1.enable(); + p2.enable(); + + loop { + cortex_m::asm::nop() + } +} diff --git a/examples/i2c-bme680.rs b/examples/i2c-bme680.rs index fd6aa9bb..83de035e 100644 --- a/examples/i2c-bme680.rs +++ b/examples/i2c-bme680.rs @@ -10,6 +10,7 @@ use hal::delay::DelayFromCountDownTimer; use hal::i2c::Config; use hal::prelude::*; use hal::stm32; +use hal::time::{ExtU32, RateExtU32}; use hal::timer::Timer; use stm32g4xx_hal as hal; @@ -32,11 +33,11 @@ fn main() -> ! { let sda = gpiob.pb9.into_alternate_open_drain(); let scl = gpiob.pb8.into_alternate_open_drain(); - let i2c = dp.I2C1.i2c(sda, scl, Config::new(100.khz()), &mut rcc); + let i2c = dp.I2C1.i2c(sda, scl, Config::new(100.kHz()), &mut rcc); let mut delayer = cp.SYST.delay(&rcc.clocks); let timer2 = Timer::new(dp.TIM2, &rcc.clocks); - let mut delay = DelayFromCountDownTimer::new(timer2.start_count_down(100.ms())); + let mut delay = DelayFromCountDownTimer::new(timer2.start_count_down(100.millis())); let mut dev = Bme680::init(i2c, &mut delayer, I2CAddress::Secondary).expect("Init function failed"); diff --git a/examples/i2c-mpu6050.rs b/examples/i2c-mpu6050.rs index b205e0bd..a0be9c10 100644 --- a/examples/i2c-mpu6050.rs +++ b/examples/i2c-mpu6050.rs @@ -6,6 +6,7 @@ use hal::i2c::Config; use hal::prelude::*; use hal::stm32; +use hal::time::{ExtU32, RateExtU32}; use stm32g4xx_hal as hal; use cortex_m_rt::entry; @@ -28,7 +29,7 @@ fn main() -> ! { let sda = gpiob.pb9.into_alternate_open_drain(); let scl = gpiob.pb8.into_alternate_open_drain(); - let i2c = dp.I2C1.i2c(sda, scl, Config::new(100.khz()), &mut rcc); + let i2c = dp.I2C1.i2c(sda, scl, Config::new(100.kHz()), &mut rcc); let mut mpu = Mpu6050::new(i2c); let mut delay = cp.SYST.delay(&rcc.clocks); @@ -39,6 +40,6 @@ fn main() -> ! { let gyro = mpu.get_gyro().expect("cannot read gyro"); let temp = mpu.get_temp().expect("cannot read temperature"); info!("acc: {:?}, gyro: {:?}, temp: {:?}C", acc, gyro, temp); - delay.delay(250.ms()); + delay.delay(250.millis()); } } diff --git a/examples/i2c.rs b/examples/i2c.rs index c2c18f08..78104767 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -6,6 +6,7 @@ use hal::i2c::Config; use hal::prelude::*; use hal::stm32; +use hal::time::RateExtU32; use stm32g4xx_hal as hal; use cortex_m_rt::entry; @@ -25,7 +26,7 @@ fn main() -> ! { let sda = gpiob.pb9.into_alternate_open_drain(); let scl = gpiob.pb8.into_alternate_open_drain(); - let mut i2c = dp.I2C1.i2c(sda, scl, Config::new(40.khz()), &mut rcc); + let mut i2c = dp.I2C1.i2c(sda, scl, Config::new(40.kHz()), &mut rcc); // Alternatively, it is possible to specify the exact timing as follows (see the documentation // of with_timing() for an explanation of the constant): //let mut i2c = dp diff --git a/examples/pwm.rs b/examples/pwm.rs index 5b08beff..8ed1d511 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -8,6 +8,7 @@ use hal::gpio::Alternate; use hal::gpio::AF6; use hal::prelude::*; use hal::stm32; +use hal::time::RateExtU32; use stm32g4xx_hal as hal; mod utils; extern crate cortex_m_rt as rt; @@ -19,7 +20,7 @@ fn main() -> ! { let gpioa = dp.GPIOA.split(&mut rcc); let pin: PA8<Alternate<AF6>> = gpioa.pa8.into_alternate(); - let mut pwm = dp.TIM1.pwm(pin, 100.hz(), &mut rcc); + let mut pwm = dp.TIM1.pwm(pin, 100.Hz(), &mut rcc); pwm.set_duty(pwm.get_max_duty() / 2); pwm.enable(); diff --git a/examples/spi-example.rs b/examples/spi-example.rs index 6d156b0b..15f8d3e3 100644 --- a/examples/spi-example.rs +++ b/examples/spi-example.rs @@ -6,8 +6,19 @@ #![no_std] use crate::hal::{ - block, delay::DelayFromCountDownTimer, gpio::gpioa::PA5, gpio::gpioa::PA6, gpio::gpioa::PA7, - gpio::Alternate, gpio::AF5, prelude::*, rcc::Config, spi, stm32::Peripherals, timer::Timer, + block, + delay::DelayFromCountDownTimer, + gpio::gpioa::PA5, + gpio::gpioa::PA6, + gpio::gpioa::PA7, + gpio::Alternate, + gpio::AF5, + prelude::*, + rcc::Config, + spi, + stm32::Peripherals, + time::{ExtU32, RateExtU32}, + timer::Timer, }; use cortex_m_rt::entry; @@ -25,7 +36,7 @@ fn main() -> ! { let rcc = dp.RCC.constrain(); let mut rcc = rcc.freeze(Config::hsi()); let timer2 = Timer::new(dp.TIM2, &rcc.clocks); - let mut delay_tim2 = DelayFromCountDownTimer::new(timer2.start_count_down(100.ms())); + let mut delay_tim2 = DelayFromCountDownTimer::new(timer2.start_count_down(100.millis())); let gpioa = dp.GPIOA.split(&mut rcc); let sclk: PA5<Alternate<AF5>> = gpioa.pa5.into_alternate(); @@ -34,7 +45,7 @@ fn main() -> ! { let mut spi = dp .SPI1 - .spi((sclk, miso, mosi), spi::MODE_0, 400.khz(), &mut rcc); + .spi((sclk, miso, mosi), spi::MODE_0, 400.kHz(), &mut rcc); let mut cs = gpioa.pa8.into_push_pull_output(); cs.set_high().unwrap(); diff --git a/examples/spi-sd.rs b/examples/spi-sd.rs index cd1281be..57a29799 100644 --- a/examples/spi-sd.rs +++ b/examples/spi-sd.rs @@ -16,6 +16,7 @@ use hal::rcc::Config; use hal::spi; use hal::stm32; use hal::stm32::Peripherals; +use hal::time::RateExtU32; use hal::timer::Timer; use stm32g4xx_hal as hal; @@ -50,7 +51,7 @@ fn main() -> ! { let mut spi = dp .SPI2 - .spi((sck, miso, mosi), spi::MODE_0, 400.khz(), &mut rcc); + .spi((sck, miso, mosi), spi::MODE_0, 400.kHz(), &mut rcc); struct Clock; diff --git a/examples/uart-dma.rs b/examples/uart-dma.rs index a80ab1d2..da734559 100644 --- a/examples/uart-dma.rs +++ b/examples/uart-dma.rs @@ -10,6 +10,7 @@ use core::fmt::Write; use hal::dma::{config::DmaConfig, stream::DMAExt, TransferExt}; use hal::prelude::*; use hal::serial::*; +use hal::time::ExtU32; use hal::{rcc, stm32}; use stm32g4xx_hal as hal; @@ -70,7 +71,7 @@ fn main() -> ! { loop { while !transfer.get_transfer_complete_flag() {} - delay_syst.delay(1000.ms()); + delay_syst.delay(1000.millis()); led.toggle().unwrap(); transfer.restart(|_tx| {}); } diff --git a/src/delay.rs b/src/delay.rs index b4b356a1..f40ffeae 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -42,11 +42,12 @@ pub use cortex_m::delay::*; use cortex_m::peripheral::SYST; use crate::nb::block; -use crate::time::{Hertz, U32Ext}; -use embedded_hal::{ - blocking::delay::{DelayMs, DelayUs}, - timer::CountDown, -}; +use crate::time::ExtU32; +use embedded_hal::blocking::delay::{DelayMs, DelayUs}; + +pub trait CountDown: embedded_hal::timer::CountDown { + fn max_period(&self) -> MicroSecond; +} pub trait SYSTDelayExt { fn delay(self, clocks: &Clocks) -> Delay; @@ -54,7 +55,7 @@ pub trait SYSTDelayExt { impl SYSTDelayExt for SYST { fn delay(self, clocks: &Clocks) -> Delay { - Delay::new(self, clocks.ahb_clk.0) + Delay::new(self, clocks.ahb_clk.raw()) } } @@ -69,7 +70,7 @@ impl DelayExt for Delay { where T: Into<MicroSecond>, { - self.delay_us(delay.into().0) + self.delay_us(delay.into().ticks()) } } @@ -94,28 +95,33 @@ macro_rules! impl_delay_from_count_down_timer { impl<T> $Delay<u32> for DelayFromCountDownTimer<T> where - T: CountDown<Time = Hertz>, + T: CountDown<Time = MicroSecond>, { fn $delay(&mut self, t: u32) { - let mut time_left = t; - - // Due to the LpTimer having only a 3 bit scaler, it is - // possible that the max timeout we can set is - // (128 * 65536) / clk_hz milliseconds. - // Assuming the fastest clk_hz = 480Mhz this is roughly ~17ms, - // or a frequency of ~57.2Hz. We use a 60Hz frequency for each - // loop step here to ensure that we stay within these bounds. - let looping_delay = $num / 60; - let looping_delay_hz = Hertz($num / looping_delay); - - self.0.start(looping_delay_hz); - while time_left > looping_delay { - block!(self.0.wait()).ok(); - time_left -= looping_delay; + let mut time_left_us = t as u64 * $num; + + let max_sleep = self.0.max_period(); + let max_sleep_us = max_sleep.to_micros() as u64; + + if time_left_us > max_sleep_us { + self.0.start(max_sleep); + + // Process the time one max_sleep duration at a time + // to avoid overflowing both u32 and the timer + for _ in 0..(time_left_us / max_sleep_us) { + block!(self.0.wait()).ok(); + time_left_us -= max_sleep_us; + } } - if time_left > 0 { - self.0.start(($num / time_left).hz()); + assert!(time_left_us <= u32::MAX as u64); + assert!(time_left_us <= max_sleep_us); + + let time_left: MicroSecond = (time_left_us as u32).micros(); + + // Only sleep + if time_left.ticks() > 0 { + self.0.start(time_left); block!(self.0.wait()).ok(); } } @@ -123,7 +129,7 @@ macro_rules! impl_delay_from_count_down_timer { impl<T> $Delay<u16> for DelayFromCountDownTimer<T> where - T: CountDown<Time = Hertz>, + T: CountDown<Time = MicroSecond>, { fn $delay(&mut self, t: u16) { self.$delay(t as u32); @@ -132,7 +138,7 @@ macro_rules! impl_delay_from_count_down_timer { impl<T> $Delay<u8> for DelayFromCountDownTimer<T> where - T: CountDown<Time = Hertz>, + T: CountDown<Time = MicroSecond>, { fn $delay(&mut self, t: u8) { self.$delay(t as u32); @@ -144,5 +150,5 @@ macro_rules! impl_delay_from_count_down_timer { impl_delay_from_count_down_timer! { (DelayMs, delay_ms, 1_000), - (DelayUs, delay_us, 1_000_000) + (DelayUs, delay_us, 1) } diff --git a/src/i2c.rs b/src/i2c.rs index e87752de..42d20aab 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -75,16 +75,16 @@ impl Config { return bits; } let speed = self.speed.unwrap(); - let (psc, scll, sclh, sdadel, scldel) = if speed.0 <= 100_000 { + let (psc, scll, sclh, sdadel, scldel) = if speed.raw() <= 100_000 { let psc = 3; - let scll = cmp::min((((i2c_clk.0 >> 1) / (psc + 1)) / speed.0) - 1, 255); + let scll = cmp::min((((i2c_clk.raw() >> 1) / (psc + 1)) / speed.raw()) - 1, 255); let sclh = scll - 4; let sdadel = 2; let scldel = 4; (psc, scll, sclh, sdadel, scldel) } else { let psc = 1; - let scll = cmp::min((((i2c_clk.0 >> 1) / (psc + 1)) / speed.0) - 1, 255); + let scll = cmp::min((((i2c_clk.raw() >> 1) / (psc + 1)) / speed.raw()) - 1, 255); let sclh = scll - 6; let sdadel = 1; let scldel = 3; diff --git a/src/pwm.rs b/src/pwm.rs index 33f61e3f..15c08f2a 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -169,9 +169,14 @@ //! This code has been taken from the stm32h7xx-hal project and modified slightly to support //! STM32G4xx MCUs. It has originally been licensed under the 0-clause BSD license. +#[cfg(any(feature = "stm32g474", feature = "stm32g484"))] +pub mod hrtim; + use core::marker::PhantomData; use core::mem::MaybeUninit; +use fugit::HertzU64; + use crate::hal; use crate::stm32::LPTIMER1; use crate::stm32::RCC; @@ -195,7 +200,7 @@ use crate::stm32::TIM5; use crate::stm32::{TIM1, TIM15, TIM16, TIM17, TIM2, TIM3, TIM4, TIM8}; use crate::rcc::{Enable, GetBusFreq, Rcc, Reset}; -use crate::time::{Hertz, NanoSecond, U32Ext}; +use crate::time::{ExtU32, Hertz, NanoSecond, RateExtU32}; #[cfg(any( feature = "stm32g471", @@ -1025,40 +1030,58 @@ pins! { ] } -// Period and prescaler calculator for 32-bit timers -// Returns (arr, psc) -fn calculate_frequency_32bit(base_freq: Hertz, freq: Hertz, alignment: Alignment) -> (u32, u16) { - let divisor = if let Alignment::Center = alignment { - freq.0 * 2 - } else { - freq.0 - }; +trait TimerType { + /// Returns (arr, psc) bits + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16); +} + +/// Any 32-bit timer +struct Timer32Bit; + +impl TimerType for Timer32Bit { + // Period and prescaler calculator for 32-bit timers + // Returns (arr, psc) bits + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16) { + let freq = HertzU64::from(freq); + let divisor = if let Alignment::Center = alignment { + freq * 2 + } else { + freq + }; - // Round to the nearest period - let arr = (base_freq.0 + (divisor >> 1)) / divisor - 1; + // Round to the nearest period + let arr = (base_freq + (divisor / 2)) / divisor - 1; - (arr, 0) + assert!(arr <= u32::MAX as u64); + + (arr as u32, 0) + } } -// Period and prescaler calculator for 16-bit timers -// Returns (arr, psc) -// Returns as (u32, u16) to be compatible but arr will always be a valid u16 -fn calculate_frequency_16bit(base_freq: Hertz, freq: Hertz, alignment: Alignment) -> (u32, u16) { - let ideal_period = calculate_frequency_32bit(base_freq, freq, alignment).0 + 1; +/// Any 16-bit timer except for HrTim +struct Timer16Bit; + +impl TimerType for Timer16Bit { + // Period and prescaler calculator for 16-bit timers + // Returns (arr, psc) + // Returns as (u32, u16) to be compatible but arr will always be a valid u16 + fn calculate_frequency(base_freq: HertzU64, freq: Hertz, alignment: Alignment) -> (u32, u16) { + let ideal_period = Timer32Bit::calculate_frequency(base_freq, freq, alignment).0 + 1; - // Division factor is (PSC + 1) - let prescale = (ideal_period - 1) / (1 << 16); + // Division factor is (PSC + 1) + let prescale = (ideal_period - 1) / (1 << 16); - // This will always fit in a 16-bit value because u32::MAX / (1 << 16) fits in a 16 bit + // This will always fit in a 16-bit value because u32::MAX / (1 << 16) fits in a 16 bit - // Round to the nearest period - let period = (ideal_period + (prescale >> 1)) / (prescale + 1) - 1; + // Round to the nearest period + let period = (ideal_period + (prescale >> 1)) / (prescale + 1) - 1; - // It should be impossible to fail these asserts - assert!(period <= 0xFFFF); - assert!(prescale <= 0xFFFF); + // It should be impossible to fail these asserts + assert!(period <= 0xFFFF); + assert!(prescale <= 0xFFFF); - (period, prescale as u16) + (period, prescale as u16) + } } // Deadtime calculator helper function @@ -1071,7 +1094,7 @@ fn calculate_deadtime(base_freq: Hertz, deadtime: NanoSecond) -> (u8, u8) { // Cortex-M7 has 32x32->64 multiply but no 64-bit divide // Divide by 100000 then 10000 by multiplying and shifting // This can't overflow because both values being multiplied are u32 - let deadtime_ticks = deadtime.0 as u64 * base_freq.0 as u64; + let deadtime_ticks = deadtime.ticks() as u64 * base_freq.raw() as u64; // Make sure we won't overflow when multiplying; DTG is max 1008 ticks and CKD is max prescaler of 4 // so deadtimes over 4032 ticks are impossible (4032*10^9 before dividing) assert!(deadtime_ticks <= 4_032_000_000_000u64); @@ -1132,68 +1155,82 @@ pub trait PwmAdvExt<WIDTH>: Sized { // Implement PwmExt trait for timer macro_rules! pwm_ext_hal { - ($TIMX:ident: $timX:ident) => { - impl PwmExt for $TIMX { + ($TIMX:ident: $timX:ident $(, $HrTimPsc:tt)*) => { + impl $(<$HrTimPsc>)* PwmExt for $TIMX { fn pwm<PINS, T, U, V>(self, pins: PINS, frequency: T, rcc: &mut Rcc) -> PINS::Channel where PINS: Pins<Self, U, V>, T: Into<Hertz>, { - $timX(self, pins, frequency.into(), rcc) + $timX::<_, _, _ $(, $HrTimPsc )*>(self, pins, frequency.into(), rcc) } } }; } -// Implement PWM configuration for timer -macro_rules! tim_hal { - ($($TIMX:ident: ($timX:ident, - $typ:ty, $bits:expr $(, DIR: $cms:ident)* $(, BDTR: $bdtr:ident, $moe_set:ident, $af1:ident, $bkinp_setting:ident $(, $bk2inp_setting:ident)*)*),)+) => { - $( - pwm_ext_hal!($TIMX: $timX); +macro_rules! simple_tim_hal { + ($TIMX:ident: ( + $timX:ident, + $bits:ident, + $(, BDTR: $bdtr:ident, $moe_set:ident)* + )) => { + pwm_ext_hal!($TIMX: $timX); + + /// Configures PWM + fn $timX<PINS, T, U>( + tim: $TIMX, + _pins: PINS, + freq: Hertz, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins<$TIMX, T, U>, + { + unsafe { + let rcc_ptr = &(*RCC::ptr()); + $TIMX::enable(rcc_ptr); + $TIMX::reset(rcc_ptr); + } - /// Configures PWM - fn $timX<PINS, T, U>( - tim: $TIMX, - _pins: PINS, - freq: Hertz, - rcc: &mut Rcc, - ) -> PINS::Channel - where - PINS: Pins<$TIMX, T, U>, - { - unsafe { - let rcc_ptr = &(*RCC::ptr()); - $TIMX::enable(rcc_ptr); - $TIMX::reset(rcc_ptr); - } + let clk = $TIMX::get_timer_frequency(&rcc.clocks); - let clk = $TIMX::get_timer_frequency(&rcc.clocks); + let (period, prescale) = <$bits>::calculate_frequency(clk.into(), freq, Alignment::Left); - let (period, prescale) = match $bits { - 16 => calculate_frequency_16bit(clk, freq, Alignment::Left), - _ => calculate_frequency_32bit(clk, freq, Alignment::Left), - }; + // Write prescale + tim.psc.write(|w| { unsafe { w.psc().bits(prescale as u16) } }); - // Write prescale - tim.psc.write(|w| { unsafe { w.psc().bits(prescale as u16) } }); + // Write period + tim.arr.write(|w| { unsafe { w.arr().bits(period.into()) } }); - // Write period - tim.arr.write(|w| { unsafe { w.arr().bits(period.into()) } }); + // BDTR: Advanced-control timers + $( + // Set CCxP = OCxREF / CCxNP = !OCxREF + // Refer to RM0433 Rev 6 - Table 324. + tim.$bdtr.write(|w| + w.moe().$moe_set() + ); + )* - // BDTR: Advanced-control timers - $( - // Set CCxP = OCxREF / CCxNP = !OCxREF - // Refer to RM0433 Rev 6 - Table 324. - tim.$bdtr.write(|w| - w.moe().$moe_set() - ); - )* + tim.cr1.write(|w| w.cen().set_bit()); - tim.cr1.write(|w| w.cen().set_bit()); + unsafe { MaybeUninit::<PINS::Channel>::uninit().assume_init() } + } + }; +} - unsafe { MaybeUninit::<PINS::Channel>::uninit().assume_init() } - } +// Implement PWM configuration for timer +macro_rules! tim_hal { + ($($TIMX:ident: ( + $timX:ident, + $typ:ty, $bits:ident $( <$HrTimPsc:tt> )* $(, DIR: $cms:ident)* + $(, BDTR: $bdtr:ident, $moe_set:ident, $af1:ident, $bkinp_setting:ident $(, $bk2inp_setting:ident)*)* + ),)+) => { + $( + simple_tim_hal!($TIMX: ( + $timX, + $bits, + $(, BDTR: $bdtr, $moe_set)* + )); impl PwmAdvExt<$typ> for $TIMX { fn pwm_advanced<PINS, CHANNEL, COMP>( @@ -1210,7 +1247,7 @@ macro_rules! tim_hal { $TIMX::reset(rcc_ptr); } - let clk = $TIMX::get_timer_frequency(&rcc.clocks).0; + let clk = $TIMX::get_timer_frequency(&rcc.clocks).raw(); PwmBuilder { _tim: PhantomData, @@ -1219,12 +1256,12 @@ macro_rules! tim_hal { _fault: PhantomData, _comp: PhantomData, alignment: Alignment::Left, - base_freq: clk.hz(), + base_freq: clk.Hz(), count: CountSettings::Explicit { period: 65535, prescaler: 0, }, bkin_enabled: false, bkin2_enabled: false, fault_polarity: Polarity::ActiveLow, - deadtime: 0.ns(), + deadtime: 0.nanos(), } } } @@ -1240,10 +1277,7 @@ macro_rules! tim_hal { let (period, prescaler) = match self.count { CountSettings::Explicit { period, prescaler } => (period as u32, prescaler), CountSettings::Frequency( freq ) => { - match $bits { - 16 => calculate_frequency_16bit(self.base_freq, freq, self.alignment), - _ => calculate_frequency_32bit(self.base_freq, freq, self.alignment), - } + <$bits>::calculate_frequency(self.base_freq.into(), freq, self.alignment) }, }; @@ -1450,10 +1484,10 @@ macro_rules! tim_hal { } tim_hal! { - TIM1: (tim1, u16, 16, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), - TIM2: (tim2, u32, 32, DIR: cms), - TIM3: (tim3, u16, 16, DIR: cms), - TIM4: (tim4, u16, 16, DIR: cms), + TIM1: (tim1, u16, Timer16Bit, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), + TIM2: (tim2, u32, Timer32Bit, DIR: cms), + TIM3: (tim3, u16, Timer16Bit, DIR: cms), + TIM4: (tim4, u16, Timer16Bit, DIR: cms), } #[cfg(any( feature = "stm32g471", @@ -1463,13 +1497,13 @@ tim_hal! { feature = "stm32g484" ))] tim_hal! { - TIM5: (tim5, u32, 32, DIR: cms), + TIM5: (tim5, u32, Timer32Bit, DIR: cms), } tim_hal! { - TIM8: (tim8, u16, 16, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), - TIM15: (tim15, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), - TIM16: (tim16, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), - TIM17: (tim17, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), + TIM8: (tim8, u16, Timer16Bit, DIR: cms, BDTR: bdtr, set_bit, af1, clear_bit, clear_bit), + TIM15: (tim15, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), + TIM16: (tim16, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), + TIM17: (tim17, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), } #[cfg(any( @@ -1481,7 +1515,7 @@ tim_hal! { feature = "stm32g4a1" ))] tim_hal! { - TIM20: (tim20, u16, 16, BDTR: bdtr, set_bit, af1, set_bit), + TIM20: (tim20, u16, Timer16Bit, BDTR: bdtr, set_bit, af1, set_bit), } pub trait PwmPinEnable { @@ -1786,8 +1820,7 @@ macro_rules! lptim_hal { $TIMX::reset(rcc_ptr); } - let clk = $TIMX::get_timer_frequency(&rcc.clocks).0; - let freq = freq.0; + let clk = $TIMX::get_timer_frequency(&rcc.clocks); let reload = clk / freq; assert!(reload < 128 * (1 << 16)); diff --git a/src/pwm/hrtim.rs b/src/pwm/hrtim.rs new file mode 100644 index 00000000..29a881e2 --- /dev/null +++ b/src/pwm/hrtim.rs @@ -0,0 +1,843 @@ +use core::marker::PhantomData; +use core::mem::MaybeUninit; + +use fugit::HertzU64; + +use crate::gpio::gpioa::{PA10, PA11, PA12, PA13, PA8, PA9}; +use crate::gpio::gpiob::{PB14, PB15}; +use crate::gpio::gpioc::{PC8, PC9}; +use crate::gpio::{Alternate, AF13, AF3}; +use crate::stm32::{ + HRTIM_COMMON, HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME, + HRTIM_TIMF, +}; + +use super::{ + ActiveHigh, Alignment, ComplementaryImpossible, FaultDisabled, Pins, Pwm, PwmPinEnable, + TimerType, +}; +use crate::rcc::{Enable, GetBusFreq, Rcc, Reset}; +use crate::stm32::RCC; +use crate::time::Hertz; + +pub struct CH1<PSCL>(PhantomData<PSCL>); +pub struct CH2<PSCL>(PhantomData<PSCL>); + +/// Internal enum that keeps track of the count settings before PWM is finalized +enum CountSettings { + Frequency(Hertz), + Period(u16), +} + +macro_rules! pins { + ($($TIMX:ty: CH1: $CH1:ty, CH2: $CH2:ty)+) => { + $( + impl<PSCL> Pins<$TIMX, CH1<PSCL>, ComplementaryImpossible> for $CH1 { + type Channel = Pwm<$TIMX, CH1<PSCL>, ComplementaryImpossible, ActiveHigh, ActiveHigh>; + } + + impl<PSCL> Pins<$TIMX, CH2<PSCL>, ComplementaryImpossible> for $CH2 { + type Channel = Pwm<$TIMX, CH2<PSCL>, ComplementaryImpossible, ActiveHigh, ActiveHigh>; + } + + impl ToHrOut for $CH1 { + type Out = HrOut1<$TIMX>; + } + + impl ToHrOut for $CH2 { + type Out = HrOut2<$TIMX>; + } + )+ + } +} + +pins! { + HRTIM_TIMA: CH1: PA8<Alternate<AF13>>, CH2: PA9<Alternate<AF13>> + + HRTIM_TIMB: CH1: PA10<Alternate<AF13>>, CH2: PA11<Alternate<AF13>> + HRTIM_TIMC: CH1: PA12<Alternate<AF13>>, CH2: PA13<Alternate<AF13>> + HRTIM_TIMD: CH1: PB14<Alternate<AF13>>, CH2: PB15<Alternate<AF13>> + + HRTIM_TIME: CH1: PC8<Alternate<AF3>>, CH2: PC9<Alternate<AF3>> + //HRTIM_TIMF: CH1: PC6<Alternate<AF13>>, CH2: PC7<Alternate<AF13>> +} + +// automatically implement Pins trait for tuples of individual pins +macro_rules! pins_tuples { + // Tuple of two pins + ($(($CHA:ident, $CHB:ident)),*) => { + $( + impl<TIM, PSCL, CHA, CHB, TA, TB> Pins<TIM, ($CHA<PSCL>, $CHB<PSCL>), (TA, TB)> for (CHA, CHB) + where + CHA: Pins<TIM, $CHA<PSCL>, TA>, + CHB: Pins<TIM, $CHB<PSCL>, TB>, + { + type Channel = (Pwm<TIM, $CHA<PSCL>, TA, ActiveHigh, ActiveHigh>, Pwm<TIM, $CHB<PSCL>, TB, ActiveHigh, ActiveHigh>); + } + + impl<PSCL> HrtimChannel<PSCL> for ($CHA<PSCL>, $CHB<PSCL>) {} + )* + }; +} + +pins_tuples! { + (CH1, CH2), + (CH2, CH1) +} + +/*pub struct Hrtimer<PSCL, TIM> { + _prescaler: PhantomData<PSCL>, + _timer: PhantomData<TIM>, + period: u16, // $perXr.perx + + cmp_value1: u16, // $cmpX1r.cmp1x + cmp_value2: u16, // $cmpX2r.cmp2x + cmp_value3: u16, // $cmpX3r.cmp3x + cmp_value4: u16, // $cmpX4r.cmp4x +}*/ + +// HrPwmExt trait +/// Allows the pwm() method to be added to the peripheral register structs from the device crate +pub trait HrPwmExt: Sized { + /// The requested frequency will be rounded to the nearest achievable frequency; the actual frequency may be higher or lower than requested. + fn pwm<PINS, T, U, V>( + self, + _pins: PINS, + frequency: T, + control: &mut HrPwmControl, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins<Self, U, V> + ToHrOut, + T: Into<Hertz>, + U: HrtimChannel<Pscl128>; +} + +pub trait HrPwmAdvExt: Sized { + fn pwm_advanced<PINS, CHANNEL, COMP>( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder<Self, Pscl128, FaultDisabled, PINS::Out> + where + PINS: Pins<Self, CHANNEL, COMP> + ToHrOut, + CHANNEL: HrtimChannel<Pscl128>; +} + +/// HrPwmBuilder is used to configure advanced HrTim PWM features +pub struct HrPwmBuilder<TIM, PSCL, FAULT, OUT> { + _tim: PhantomData<TIM>, + _fault: PhantomData<FAULT>, + _prescaler: PhantomData<PSCL>, + _out: PhantomData<OUT>, + alignment: Alignment, + base_freq: HertzU64, + count: CountSettings, + enable_push_pull: bool, + //bkin_enabled: bool, // If the FAULT type parameter is FaultEnabled, either bkin or bkin2 must be enabled + //bkin2_enabled: bool, + //fault_polarity: Polarity, + //deadtime: NanoSecond, +} + +pub trait HrCompareRegister { + fn get_duty(&self) -> u16; + fn set_duty(&mut self, duty: u16); +} + +pub struct HrCr1<TIM>(PhantomData<TIM>); +pub struct HrCr2<TIM>(PhantomData<TIM>); +pub struct HrCr3<TIM>(PhantomData<TIM>); +pub struct HrCr4<TIM>(PhantomData<TIM>); + +pub struct HrTim<TIM, PSCL> { + _timer: PhantomData<TIM>, + _prescaler: PhantomData<PSCL>, +} + +pub trait HrTimer<TIM, PSCL> { + /// Get period of timer in number of ticks + /// + /// This is also the maximum duty usable for `HrCompareRegister::set_duty` + fn get_period(&self) -> u16; + + /// Set period of timer in number of ticks + /// + /// NOTE: This will affect the maximum duty usable for `HrCompareRegister::set_duty` + fn set_period(&mut self, period: u16); +} + +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: EventSource); + + /// Stop listening to the specified event + fn disable_set_event(&mut self, set_event: EventSource); + + /// 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: EventSource); + + /// Stop listening to the specified event + fn disable_rst_event(&mut self, reset_event: EventSource); + + /// Get current state of the output + fn get_state(&self) -> State; +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum State { + Idle, + Running, + Fault, +} + +pub enum EventSource { + /// Compare match with compare register 1 of this timer + Cr1, + + /// Compare match with compare register 2 of this timer + Cr2, + + /// Compare match with compare register 3 of this timer + Cr3, + + /// Compare match with compare register 4 of this timer + Cr4, + + Period, + /* + /// Compare match with compare register 1 of master timer + MasterCr1, + + /// Compare match with compare register 2 of master timer + MasterCr2, + + /// Compare match with compare register 3 of master timer + MasterCr3, + + /// Compare match with compare register 4 of master timer + MasterCr4, + + MasterPeriod,*/ + // TODO: These are unique for every timer output + //Extra(E) +} +pub trait ToHrOut { + type Out; +} + +impl<PA, PB> ToHrOut for (PA, PB) +where + PA: ToHrOut, + PB: ToHrOut, +{ + type Out = (PA::Out, PB::Out); +} + +pub struct HrOut1<TIM>(PhantomData<TIM>); +pub struct HrOut2<TIM>(PhantomData<TIM>); + +// Implement PWM configuration for timer +macro_rules! hrtim_hal { + ($($TIMX:ident: ($timX:ident, $timXcr:ident, $perXr:ident, $tXcen:ident),)+) => { + $( + + // Implement HrPwmExt trait for hrtimer + impl HrPwmExt for $TIMX { + fn pwm<PINS, T, U, V>( + self, + pins: PINS, + frequency: T, + control: &mut HrPwmControl, + rcc: &mut Rcc, + ) -> PINS::Channel + where + PINS: Pins<Self, U, V> + ToHrOut, + T: Into<Hertz>, + U: HrtimChannel<Pscl128>, + { + let _= self.pwm_advanced(pins, rcc).frequency(frequency).finalize(control); + + unsafe { MaybeUninit::<PINS::Channel>::uninit().assume_init() } + } + } + + impl HrPwmAdvExt for $TIMX { + fn pwm_advanced<PINS, CHANNEL, COMP>( + self, + _pins: PINS, + rcc: &mut Rcc, + ) -> HrPwmBuilder<Self, Pscl128, FaultDisabled, PINS::Out> + where + PINS: Pins<Self, CHANNEL, COMP> + ToHrOut, + CHANNEL: HrtimChannel<Pscl128> + { + // 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, + _fault: PhantomData, + _prescaler: PhantomData, + _out: PhantomData, + alignment: Alignment::Left, + base_freq: clk, + count: CountSettings::Period(u16::MAX), + enable_push_pull: false, + //bkin_enabled: false, + //bkin2_enabled: false, + //fault_polarity: Polarity::ActiveLow, + //deadtime: 0.nanos(), + } + } + } + + impl<PSCL, FAULT, OUT> + HrPwmBuilder<$TIMX, PSCL, FAULT, OUT> + where + PSCL: HrtimPrescaler, + { + pub fn finalize(self, _control: &mut HrPwmControl) -> (HrTim<$TIMX, PSCL>, (HrCr1<$TIMX>, HrCr2<$TIMX>, HrCr3<$TIMX>, HrCr4<$TIMX>), OUT) { + let tim = unsafe { &*$TIMX::ptr() }; + + let (period, prescaler_bits) = match self.count { + CountSettings::Period(period) => (period as u32, PSCL::BITS as u16), + CountSettings::Frequency( freq ) => { + <TimerHrTim<PSCL>>::calculate_frequency(self.base_freq, freq, self.alignment) + }, + }; + + // Write prescaler and any special modes + tim.$timXcr.write(|w| unsafe { + w + // Enable Continous mode + .cont().set_bit() + + // Push-Pull mode + .pshpll().bit(self.enable_push_pull) + + // TODO: add support for more modes + + // Set prescaler + .ck_pscx().bits(prescaler_bits as u8) + }); + + // Write period + tim.$perXr.write(|w| unsafe { w.perx().bits(period as u16) }); + + // Start timer + cortex_m::interrupt::free(|_| { + let master = unsafe { &*HRTIM_MASTER::ptr() }; + master.mcr.modify(|_r, w| { w.$tXcen().set_bit() }); + }); + + unsafe { + MaybeUninit::uninit().assume_init() + } + } + + /// Set the PWM frequency; will overwrite the previous prescaler and period + /// The requested frequency will be rounded to the nearest achievable frequency; the actual frequency may be higher or lower than requested. + pub fn frequency<T: Into<Hertz>>(mut self, freq: T) -> Self { + self.count = CountSettings::Frequency( freq.into() ); + + self + } + + /// Set the prescaler; PWM count runs at base_frequency/(prescaler+1) + pub fn prescaler<P>(self, _prescaler: P) -> HrPwmBuilder<$TIMX, P, FAULT, OUT> + where + P: HrtimPrescaler, + { + let HrPwmBuilder { + _tim, + _fault, + _prescaler: _, + _out, + enable_push_pull, + alignment, + base_freq, + count, + } = self; + + let period = match count { + CountSettings::Frequency(_) => u16::MAX, + CountSettings::Period(period) => period, + }; + + let count = CountSettings::Period(period); + + HrPwmBuilder { + _tim, + _fault, + _prescaler: PhantomData, + _out, + enable_push_pull, + alignment, + base_freq, + count, + } + } + + /// 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 + } + + /// 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 { + self.enable_push_pull = enable; + + self + } + + //pub fn half_mode(mut self, enable: bool) -> Self + + //pub fn interleaved_mode(mut self, mode: _) -> Self + + //pub fn swap_mode(mut self, enable: bool) -> Self + } + )+ + } +} + +macro_rules! hrtim_pin_hal { + ($($TIMX:ident: + ($CH:ident, $perXr:ident, $cmpXYr:ident, $cmpYx:ident, $cmpY:ident, $tXYoen:ident, $tXYodis:ident, $setXYr:ident, $rstXYr:ident),)+ + ) => { + $( + impl<PSCL, COMP, POL, NPOL> hal::PwmPin for Pwm<$TIMX, $CH<PSCL>, COMP, POL, NPOL> + where Pwm<$TIMX, $CH<PSCL>, COMP, POL, NPOL>: PwmPinEnable { + type Duty = u16; + + // You may not access self in the following methods! + // See unsafe above + + fn disable(&mut self) { + self.ccer_disable(); + } + + fn enable(&mut self) { + self.ccer_enable(); + } + + fn get_duty(&self) -> Self::Duty { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.read().$cmpYx().bits() + } + + fn get_max_duty(&self) -> Self::Duty { + let tim = unsafe { &*$TIMX::ptr() }; + + let arr = tim.$perXr.read().perx().bits(); + + // One PWM cycle is ARR+1 counts long + // Valid PWM duty cycles are 0 to ARR+1 + // However, if ARR is 65535 on a 16-bit timer, we can't add 1 + // In that case, 100% duty cycle is not possible, only 65535/65536 + if arr == Self::Duty::MAX { + arr + } + else { + arr + 1 + } + } + + fn set_duty(&mut self, duty: Self::Duty) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.write(|w| unsafe { w.$cmpYx().bits(duty) }); + } + } + + // Enable implementation for ComplementaryImpossible + impl<POL, NPOL, PSCL> PwmPinEnable for Pwm<$TIMX, $CH<PSCL>, ComplementaryImpossible, POL, NPOL> { + fn ccer_enable(&mut self) { + let tim = unsafe { &*$TIMX::ptr() }; + // Select period as a SET-event + tim.$setXYr.write(|w| { w.per().set_bit() } ); + + // Select cmpY as a RESET-event + tim.$rstXYr.write(|w| { w.$cmpY().set_bit() } ); + + // TODO: Should this part only be in Pwm::enable? + // Enable output Y on channel X + // This is a set-only register, no risk for data race + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.oenr.write(|w| { w.$tXYoen().set_bit() }); + } + fn ccer_disable(&mut self) { + let tim = unsafe { &*$TIMX::ptr() }; + // Clear SET-events + tim.$setXYr.reset(); + + // TODO: Should this part only be in Pwm::disable + // Disable output Y on channel X + // This is a write only register, no risk for data race + let common = unsafe { &*HRTIM_COMMON::ptr() }; + common.odisr.write(|w| { w.$tXYodis().set_bit() }); + } + } + )+ + } +} + +macro_rules! hrtim_out_common { + ($TIMX:ident, $set_event:ident, $register:ident, $action:ident) => {{ + let tim = unsafe { &*$TIMX::ptr() }; + + match $set_event { + EventSource::Cr1 => tim.$register.modify(|_r, w| w.cmp1().$action()), + EventSource::Cr2 => tim.$register.modify(|_r, w| w.cmp2().$action()), + EventSource::Cr3 => tim.$register.modify(|_r, w| w.cmp3().$action()), + EventSource::Cr4 => tim.$register.modify(|_r, w| w.cmp4().$action()), + EventSource::Period => tim.$register.modify(|_r, w| w.per().$action()), + } + }}; +} + +macro_rules! hrtim_out { + ($($TIMX:ident: $out_type:ident: $tXYoen:ident, $tXYodis:ident, $tXYods:ident, $setXYr:ident, $rstXYr:ident,)+) => {$( + impl HrOutput for $out_type<$TIMX> { + 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: EventSource) { + hrtim_out_common!($TIMX, set_event, $setXYr, set_bit) + } + fn disable_set_event(&mut self, set_event: EventSource) { + hrtim_out_common!($TIMX, set_event, $setXYr, clear_bit) + } + + fn enable_rst_event(&mut self, reset_event: EventSource) { + hrtim_out_common!($TIMX, reset_event, $rstXYr, set_bit) + } + fn disable_rst_event(&mut self, reset_event: EventSource) { + hrtim_out_common!($TIMX, reset_event, $rstXYr, clear_bit) + } + + fn get_state(&self) -> State { + let ods; + let oen; + + unsafe { + let common = &*HRTIM_COMMON::ptr(); + ods = common.odsr.read().$tXYods().bit_is_set(); + oen = common.oenr.read().$tXYoen().bit_is_set(); + } + + match (oen, ods) { + (true, _) => State::Running, + (false, false) => State::Idle, + (false, true) => State::Fault + } + } + } + )+}; +} + +hrtim_out! { + HRTIM_TIMA: HrOut1: ta1oen, ta1odis, ta1ods, seta1r, rsta1r, + HRTIM_TIMA: HrOut2: ta2oen, ta1odis, ta2ods, seta2r, rsta2r, + + HRTIM_TIMB: HrOut1: tb1oen, tb1odis, tb1ods, setb1r, rstb1r, + HRTIM_TIMB: HrOut2: tb2oen, tb2odis, tb2ods, setb2r, rstb2r, + + HRTIM_TIMC: HrOut1: tc1oen, tc1odis, tc1ods, setc1r, rstc1r, + HRTIM_TIMC: HrOut2: tc2oen, tc2odis, tc2ods, setc2r, rstc2r, + + HRTIM_TIMD: HrOut1: td1oen, td1odis, td1ods, setd1r, rstd1r, + HRTIM_TIMD: HrOut2: td2oen, td2odis, td2ods, setd2r, rstd2r, + + HRTIM_TIME: HrOut1: te1oen, te1odis, te1ods, sete1r, rste1r, + HRTIM_TIME: HrOut2: te2oen, te2odis, te2ods, sete2r, rste2r, + + // TODO: Somehow, there is no rstf1r + //HRTIM_TIMF: HrOut1: tf1oen, tf1odis, tf1ods, setf1r, rstf1r, + + // TODO: Somehow, there is no tf2oen + //HRTIM_TIMF: HrOut2: tf2oen, tf2odis, tf2ods, setf2r, rstf2r, +} + +macro_rules! hrtim_cr_helper { + ($TIMX:ident: $cr_type:ident: $cmpXYr:ident, $cmpYx:ident) => { + impl HrCompareRegister for $cr_type<$TIMX> { + fn get_duty(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.read().$cmpYx().bits() + } + fn set_duty(&mut self, duty: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$cmpXYr.write(|w| unsafe { w.$cmpYx().bits(duty) }); + } + } + }; +} + +macro_rules! hrtim_cr { + ($($TIMX:ident: [$cmpX1r:ident, $cmpX2r:ident, $cmpX3r:ident, $cmpX4r:ident],)+) => {$( + hrtim_cr_helper!($TIMX: HrCr1: $cmpX1r, cmp1x); + hrtim_cr_helper!($TIMX: HrCr2: $cmpX2r, cmp2x); + hrtim_cr_helper!($TIMX: HrCr3: $cmpX3r, cmp3x); + hrtim_cr_helper!($TIMX: HrCr4: $cmpX4r, cmp4x); + )+}; +} + +macro_rules! hrtim_timer { + ($($TIMX:ident: $perXr:ident,)+) => {$( + impl<PSCL> HrTimer<$TIMX, PSCL> for HrTim<$TIMX, PSCL> { + fn get_period(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$perXr.read().perx().bits() + } + fn set_period(&mut self, period: u16) { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.$perXr.write(|w| unsafe { w.perx().bits(period as u16) }); + } + } + )+}; +} + +hrtim_timer! { + HRTIM_TIMA: perar, + HRTIM_TIMB: perbr, + HRTIM_TIMC: percr, + HRTIM_TIMD: perdr, + HRTIM_TIME: perer, + HRTIM_TIMF: perfr, +} + +hrtim_cr! { + HRTIM_TIMA: [cmp1ar, cmp2ar, cmp3ar, cmp4ar], + HRTIM_TIMB: [cmp1br, cmp2br, cmp3br, cmp4br], + HRTIM_TIMC: [cmp1cr, cmp2cr, cmp3cr, cmp4cr], + HRTIM_TIMD: [cmp1dr, cmp2dr, cmp3dr, cmp4dr], + HRTIM_TIME: [cmp1er, cmp2er, cmp3er, cmp4er], + HRTIM_TIMF: [cmp1fr, cmp2fr, cmp3fr, cmp4fr], +} + +hrtim_hal! { + // TODO: HRTIM_MASTER + HRTIM_TIMA: (hrtim_tima, timacr, perar, tacen), + HRTIM_TIMB: (hrtim_timb, timbcr, perbr, tbcen), + HRTIM_TIMC: (hrtim_timc, timccr, percr, tccen), + HRTIM_TIMD: (hrtim_timd, timdcr, perdr, tdcen), + HRTIM_TIME: (hrtim_time, timecr, perer, tecen), + + // TODO: why is there no rstf1r? + //HRTIM_TIMF: (hrtim_timf1, timfcr, perfr, tfcen, setf1r, rstf1r, cmp1), + HRTIM_TIMF: (hrtim_timf, timfcr, perfr, tfcen), +} + +hrtim_pin_hal! { + HRTIM_TIMA: (CH1, perar, cmp1ar, cmp1x, cmp1, ta1oen, ta1odis, seta1r, rsta1r), + HRTIM_TIMA: (CH2, perar, cmp3ar, cmp3x, cmp3, ta2oen, ta2odis, seta2r, rsta2r), + + HRTIM_TIMB: (CH1, perbr, cmp1br, cmp1x, cmp1, tb1oen, tb1odis, setb1r, rstb1r), + HRTIM_TIMB: (CH2, perbr, cmp3br, cmp3x, cmp3, tb2oen, tb2odis, setb2r, rstb2r), + + HRTIM_TIMC: (CH1, percr, cmp1cr, cmp1x, cmp1, tc1oen, tc1odis, setc1r, rstc1r), + HRTIM_TIMC: (CH2, percr, cmp3cr, cmp3x, cmp3, tc2oen, tc2odis, setc2r, rstc2r), + + + HRTIM_TIMD: (CH1, perdr, cmp1dr, cmp1x, cmp1, td1oen, td1odis, setd1r, rstd1r), + HRTIM_TIMD: (CH2, perdr, cmp3dr, cmp3x, cmp3, td2oen, td2odis, setd2r, rstd2r), + + HRTIM_TIME: (CH1, perer, cmp1er, cmp1x, cmp1, te1oen, te1odis, sete1r, rste1r), + HRTIM_TIME: (CH2, perer, cmp3er, cmp3x, cmp3, te2oen, te2odis, sete2r, rste2r), + + // TODO: tf1oen and rstf1r are not defined + //HRTIM_TIMF: (CH1, perfr, cmp1fr, cmp1x, cmp1, tf1oen, tf1odis, setf1r, rstf1r), + + // TODO: tf2oen is not defined + //HRTIM_TIMF: (CH2, perfr, cmp3fr, cmp3x, cmp3, tf2oen, tf2odis, setf2r, rstf2r), +} + +pub trait HrtimPrescaler { + const BITS: u8; + const VALUE: u8; +} + +macro_rules! impl_pscl { + ($($t:ident => $b:literal, $c:literal,)+) => {$( + #[derive(Copy, Clone)] + pub struct $t; + impl HrtimPrescaler for $t { + const BITS: u8 = $b; + const VALUE: u8 = $c; + } + )+}; +} + +impl_pscl! { + Pscl1 => 0b000, 1, + Pscl2 => 0b001, 2, + Pscl4 => 0b010, 4, + Pscl8 => 0b011, 8, + Pscl16 => 0b100, 16, + Pscl32 => 0b101, 32, + Pscl64 => 0b110, 64, + Pscl128 => 0b111, 128, +} + +/// HrTim timer +struct TimerHrTim<PSC>(PhantomData<PSC>); + +impl<PSC: HrtimPrescaler> super::TimerType for TimerHrTim<PSC> { + // 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 = super::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()) + } +} + +pub trait HrtimChannel<PSCL> {} + +impl<PSCL> HrtimChannel<PSCL> for CH1<PSCL> {} +impl<PSCL> HrtimChannel<PSCL> for CH2<PSCL> {} + +pub struct HrPwmControl { + _x: PhantomData<()>, +} + +/// The divsion ratio between f_hrtim and the fault signal sampling clock for digital filters +pub enum FaultSamplingClkDiv { + /// No division + /// + /// fault signal sampling clock = f_hrtim + None = 0b00, + + /// 1/2 + /// + /// fault signal sampling clock = f_hrtim / 2 + Two = 0b01, + + /// 1/4 + /// + /// fault signal sampling clock = f_hrtim / 4 + Four = 0b10, + + /// 1/8 + /// + /// fault signal sampling clock = f_hrtim / 8 + Eight = 0b11, +} + +pub struct FaultInputs { + _x: (), +} + +pub trait HrControltExt { + fn hr_control(self, _rcc: &mut Rcc) -> HrTimOngoingCalibration; +} + +impl HrControltExt for HRTIM_COMMON { + fn hr_control(self, _rcc: &mut Rcc) -> HrTimOngoingCalibration { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + unsafe { + let rcc_ptr = &*RCC::ptr(); + + HRTIM_COMMON::enable(rcc_ptr); + HRTIM_COMMON::reset(rcc_ptr); + } + + // Start calibration procedure + common + .dllcr + .write(|w| w.cal().set_bit().calen().clear_bit()); + + HrTimOngoingCalibration { + divider: FaultSamplingClkDiv::None, + } + } +} + +pub struct HrTimOngoingCalibration { + divider: FaultSamplingClkDiv, +} + +impl HrTimOngoingCalibration { + /// SAFETY: Calibration needs to be done before calling this + unsafe fn init(self) -> (HrPwmControl, FaultInputs) { + let common = unsafe { &*HRTIM_COMMON::ptr() }; + + unsafe { + // Enable periodic calibration + // with f_hrtim at 170MHz, these settings leads to + // a period of about 6.2ms + common + .dllcr + .modify(|_r, w| w.calrte().bits(0b00).cal().set_bit().calen().clear_bit()); + common.fltinr2.write(|w| w.fltsd().bits(self.divider as u8)); + } + + (HrPwmControl { _x: PhantomData }, FaultInputs { _x: () }) + } + + pub fn wait_for_calibration(self) -> (HrPwmControl, FaultInputs) { + 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() } + } +} diff --git a/src/rcc/enable.rs b/src/rcc/enable.rs index 40e26b73..beb4ac18 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -200,10 +200,5 @@ bus! { #[cfg(any(feature = "stm32g474", feature = "stm32g484"))] bus! { - HRTIM_TIMA => (APB2, 26), - HRTIM_TIMB => (APB2, 26), - HRTIM_TIMC => (APB2, 26), - HRTIM_TIMD => (APB2, 26), - HRTIM_TIME => (APB2, 26), - HRTIM_TIMF => (APB2, 26), + HRTIM_COMMON => (APB2, 26), } diff --git a/src/rcc/mod.rs b/src/rcc/mod.rs index 0b8279ab..c689000d 100644 --- a/src/rcc/mod.rs +++ b/src/rcc/mod.rs @@ -1,5 +1,5 @@ use crate::stm32::{rcc, FLASH, PWR, RCC}; -use crate::time::{Hertz, U32Ext}; +use crate::time::{Hertz, RateExtU32}; mod clockout; mod config; @@ -47,7 +47,7 @@ pub struct PLLClocks { impl Default for Clocks { fn default() -> Clocks { - let freq = HSI_FREQ.hz(); + let freq = HSI_FREQ.Hz(); Clocks { sys_clk: freq, ahb_clk: freq, @@ -80,7 +80,7 @@ impl Rcc { let (sys_clk, sw_bits) = match rcc_cfg.sys_mux { SysClockSrc::HSI => { self.enable_hsi(); - (HSI_FREQ.hz(), 0b01) + (HSI_FREQ.Hz(), 0b01) } SysClockSrc::HSE(freq) => { self.enable_hse(false); @@ -95,7 +95,7 @@ impl Rcc { } }; - let sys_freq = sys_clk.0; + let sys_freq = sys_clk.raw(); let (ahb_freq, ahb_psc_bits) = match rcc_cfg.ahb_psc { Prescaler::Div2 => (sys_freq / 2, 0b1000), Prescaler::Div4 => (sys_freq / 4, 0b1001), @@ -105,30 +105,30 @@ impl Rcc { Prescaler::Div128 => (sys_freq / 128, 0b1101), Prescaler::Div256 => (sys_freq / 256, 0b1110), Prescaler::Div512 => (sys_freq / 512, 0b1111), - _ => (sys_clk.0, 0b0000), + _ => (sys_freq, 0b0000), }; let (apb1_freq, apb1_psc_bits) = match rcc_cfg.apb1_psc { - Prescaler::Div2 => (sys_clk.0 / 2, 0b100), - Prescaler::Div4 => (sys_clk.0 / 4, 0b101), - Prescaler::Div8 => (sys_clk.0 / 8, 0b110), - Prescaler::Div16 => (sys_clk.0 / 16, 0b111), - _ => (sys_clk.0, 0b000), + Prescaler::Div2 => (sys_freq / 2, 0b100), + Prescaler::Div4 => (sys_freq / 4, 0b101), + Prescaler::Div8 => (sys_freq / 8, 0b110), + Prescaler::Div16 => (sys_freq / 16, 0b111), + _ => (sys_freq, 0b000), }; let (apb2_freq, apb2_psc_bits) = match rcc_cfg.apb2_psc { - Prescaler::Div2 => (sys_clk.0 / 2, 0b100), - Prescaler::Div4 => (sys_clk.0 / 4, 0b101), - Prescaler::Div8 => (sys_clk.0 / 8, 0b110), - Prescaler::Div16 => (sys_clk.0 / 16, 0b111), - _ => (sys_clk.0, 0b000), + Prescaler::Div2 => (sys_freq / 2, 0b100), + Prescaler::Div4 => (sys_freq / 4, 0b101), + Prescaler::Div8 => (sys_freq / 8, 0b110), + Prescaler::Div16 => (sys_freq / 16, 0b111), + _ => (sys_freq, 0b000), }; unsafe { // Adjust flash wait states let flash = &(*FLASH::ptr()); flash.acr.modify(|_, w| { - w.latency().bits(if sys_clk.0 <= 24_000_000 { + w.latency().bits(if sys_freq <= 24_000_000 { 0b000 - } else if sys_clk.0 <= 48_000_000 { + } else if sys_freq <= 48_000_000 { 0b001 } else { 0b010 @@ -169,12 +169,12 @@ impl Rcc { clocks: Clocks { pll_clk, sys_clk, - core_clk: ahb_freq.hz(), - ahb_clk: ahb_freq.hz(), - apb1_clk: apb1_freq.hz(), - apb1_tim_clk: apb1_tim_clk.hz(), - apb2_clk: apb2_freq.hz(), - apb2_tim_clk: apb2_tim_clk.hz(), + core_clk: ahb_freq.Hz(), + ahb_clk: ahb_freq.Hz(), + apb1_clk: apb1_freq.Hz(), + apb1_tim_clk: apb1_tim_clk.Hz(), + apb2_clk: apb2_freq.Hz(), + apb2_tim_clk: apb2_tim_clk.Hz(), }, } } @@ -198,11 +198,11 @@ impl Rcc { } PLLSrc::HSE(freq) => { self.enable_hse(false); - (freq.0, 0b11) + (freq.raw(), 0b11) } PLLSrc::HSE_BYPASS(freq) => { self.enable_hse(true); - (freq.0, 0b11) + (freq.raw(), 0b11) } }; @@ -212,15 +212,15 @@ impl Rcc { // Calculate the output frequencies for the P, Q, and R outputs let p = pll_cfg .p - .map(|p| ((pll_freq / p.divisor()).hz(), p.register_setting())); + .map(|p| ((pll_freq / p.divisor()).Hz(), p.register_setting())); let q = pll_cfg .q - .map(|q| ((pll_freq / q.divisor()).hz(), q.register_setting())); + .map(|q| ((pll_freq / q.divisor()).Hz(), q.register_setting())); let r = pll_cfg .r - .map(|r| ((pll_freq / r.divisor()).hz(), r.register_setting())); + .map(|r| ((pll_freq / r.divisor()).Hz(), r.register_setting())); // Set the M input divider, the N multiplier for the PLL, and the PLL source. self.rb.pllcfgr.modify(|_, w| unsafe { diff --git a/src/serial/usart.rs b/src/serial/usart.rs index d8cfe6c3..d5779131 100644 --- a/src/serial/usart.rs +++ b/src/serial/usart.rs @@ -457,7 +457,7 @@ macro_rules! uart_lp { // try SYSCLK if PCLK is not high enough. We could also select 8x oversampling // instead of 16x. - let clk = <$USARTX as RccBus>::Bus::get_frequency(&rcc.clocks).0 as u64; + let clk = <$USARTX as RccBus>::Bus::get_frequency(&rcc.clocks).raw() as u64; let bdr = config.baudrate.0 as u64; let div = ($clk_mul * clk) / bdr; if div < 16 { @@ -604,7 +604,7 @@ macro_rules! uart_full { // try SYSCLK if PCLK is not high enough. We could also select 8x oversampling // instead of 16x. - let clk = <$USARTX as RccBus>::Bus::get_frequency(&rcc.clocks).0 as u64; + let clk = <$USARTX as RccBus>::Bus::get_frequency(&rcc.clocks).raw() as u64; let bdr = config.baudrate.0 as u64; let clk_mul = 1; let div = (clk_mul * clk) / bdr; diff --git a/src/spi.rs b/src/spi.rs index a70dda7a..e5cd0d5a 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -114,8 +114,8 @@ macro_rules! spi { // disable SS output spi.cr2.write(|w| w.ssoe().clear_bit()); - let spi_freq = speed.into().0; - let bus_freq = <$SPIX as RccBus>::Bus::get_frequency(&rcc.clocks).0; + let spi_freq = speed.into().raw(); + let bus_freq = <$SPIX as RccBus>::Bus::get_frequency(&rcc.clocks).raw(); let br = match bus_freq / spi_freq { 0 => unreachable!(), 1..=2 => 0b000, diff --git a/src/time.rs b/src/time.rs index 7beeb57a..dabf2993 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,43 +1,84 @@ -use core::ops::{Add, Div}; +pub use fugit::{ + Duration, ExtU32, HertzU32 as Hertz, HoursDurationU32 as Hour, + MicrosDurationU32 as MicroSecond, MinutesDurationU32 as Minute, NanosDurationU32 as NanoSecond, + RateExtU32, SecsDurationU32 as Second, +}; + +/// Baudrate +#[derive(Debug, Eq, PartialEq, PartialOrd, Clone, Copy)] +pub struct Bps(pub u32); /// A measurement of a monotonically nondecreasing clock -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] -pub struct Instant(pub u32); - -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] -pub struct Bps(pub u32); +pub type Instant = fugit::TimerInstantU32<1_000_000>; + +/// WeekDay (1-7) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct WeekDay(pub u32); + +/// Date (1-31) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MonthDay(pub u32); + +/// Week (1-52) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Week(pub u32); + +/// Month (1-12) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Month(pub u32); + +/// Year +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Year(pub u32); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Time { + pub hours: u32, + pub minutes: u32, + pub seconds: u32, + pub daylight_savings: bool, +} -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] -pub struct Hertz(pub u32); +impl Time { + pub fn new(hours: Hour, minutes: Minute, seconds: Second, daylight_savings: bool) -> Self { + Self { + hours: hours.ticks(), + minutes: minutes.ticks(), + seconds: seconds.ticks(), + daylight_savings, + } + } +} -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] -pub struct MicroSecond(pub u32); +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Date { + pub day: u32, + pub month: u32, + pub year: u32, +} -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] -pub struct NanoSecond(pub u32); +impl Date { + pub fn new(year: Year, month: Month, day: MonthDay) -> Self { + Self { + day: day.0, + month: month.0, + year: year.0, + } + } +} -/// Extension trait that adds convenience methods to the `u32` type pub trait U32Ext { /// Wrap in `Bps` fn bps(self) -> Bps; - /// Wrap in `Hertz` - fn hz(self) -> Hertz; - - /// Wrap in `Hertz` - fn khz(self) -> Hertz; - - /// Wrap in `Hertz` - fn mhz(self) -> Hertz; - - /// Wrap in `NanoSecond` - fn ns(self) -> NanoSecond; + /// Day in month + fn day(self) -> MonthDay; - /// Wrap in `MicroSecond` - fn us(self) -> MicroSecond; + /// Month + fn month(self) -> Month; - /// Wrap in `MicroSecond` - fn ms(self) -> MicroSecond; + /// Year + fn year(self) -> Year; } impl U32Ext for u32 { @@ -45,126 +86,30 @@ impl U32Ext for u32 { assert!(self > 0); Bps(self) } - - fn hz(self) -> Hertz { - assert!(self > 0); - Hertz(self) + fn day(self) -> MonthDay { + MonthDay(self) } - fn khz(self) -> Hertz { - Hertz(self.saturating_mul(1_000)) + fn month(self) -> Month { + Month(self) } - fn mhz(self) -> Hertz { - Hertz(self.saturating_mul(1_000_000)) - } - - fn ms(self) -> MicroSecond { - MicroSecond(self.saturating_mul(1_000)) - } - - fn us(self) -> MicroSecond { - MicroSecond(self) - } - - fn ns(self) -> NanoSecond { - NanoSecond(self) - } -} - -impl Hertz { - pub fn duration(self, cycles: u32) -> MicroSecond { - let cycles = cycles as u64; - let clk = self.0 as u64; - let us = cycles.saturating_mul(1_000_000_u64) / clk; - MicroSecond(us as u32) + fn year(self) -> Year { + Year(self) } } -impl Add for Hertz { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0) - } -} - -impl Div for Hertz { - type Output = u32; - - fn div(self, other: Self) -> Self::Output { - self.0 / other.0 - } +pub fn duration(hz: Hertz, cycles: u32) -> MicroSecond { + let cycles = cycles as u64; + let clk = hz.raw() as u64; + let us = cycles.saturating_mul(1_000_000_u64) / clk; + MicroSecond::from_ticks(us as u32) } -impl Div<u32> for Hertz { - type Output = Hertz; - - fn div(self, other: u32) -> Self::Output { - Self(self.0 / other) - } -} - -impl MicroSecond { - pub fn cycles(self, clk: Hertz) -> u32 { - assert!(self.0 > 0); - let clk = clk.0 as u64; - let period = self.0 as u64; - let cycles = clk.saturating_mul(period) / 1_000_000_u64; - cycles as u32 - } -} - -impl Add for MicroSecond { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0) - } -} - -impl From<Hertz> for MicroSecond { - fn from(freq: Hertz) -> MicroSecond { - assert!(freq.0 <= 1_000_000); - MicroSecond(1_000_000 / freq.0) - } -} - -impl From<MicroSecond> for Hertz { - fn from(period: MicroSecond) -> Hertz { - assert!(period.0 > 0 && period.0 <= 1_000_000); - Hertz(1_000_000 / period.0) - } -} - -impl NanoSecond { - pub fn cycles(self, clk: Hertz) -> u32 { - assert!(self.0 > 0); - let clk = clk.0 as u64; - let period = self.0 as u64; - let cycles = clk.saturating_mul(period) / 1_000_000_000u64; - cycles as u32 - } -} - -impl Add for NanoSecond { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0) - } -} - -impl From<Hertz> for NanoSecond { - fn from(freq: Hertz) -> NanoSecond { - assert!(freq.0 <= 1_000_000_000); - NanoSecond(1_000_000_000 / freq.0) - } -} - -impl From<NanoSecond> for Hertz { - fn from(period: NanoSecond) -> Hertz { - assert!(period.0 > 0 && period.0 <= 1_000_000_000); - Hertz(1_000_000_000 / period.0) - } +pub fn cycles(ms: MicroSecond, clk: Hertz) -> u32 { + assert!(ms.ticks() > 0); + let clk = clk.raw() as u64; + let period = ms.ticks() as u64; + let cycles = clk.saturating_mul(period) / 1_000_000_u64; + cycles as u32 } diff --git a/src/timer.rs b/src/timer.rs index 618785df..47584e76 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -3,16 +3,17 @@ //! Pins can be used for PWM output in both push-pull mode (`Alternate`) and open-drain mode //! (`AlternateOD`). +use crate::delay::CountDown; use cast::{u16, u32}; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::{DCB, DWT, SYST}; -use embedded_hal::timer::{Cancel, CountDown, Periodic}; +use embedded_hal::timer::{Cancel, CountDown as _, Periodic}; use void::Void; use crate::stm32::RCC; use crate::rcc::{self, Clocks}; -use crate::time::Hertz; +use crate::time::{Hertz, MicroSecond}; /// Timer wrapper pub struct Timer<TIM> { @@ -28,12 +29,12 @@ pub struct CountDownTimer<TIM> { impl<TIM> Timer<TIM> where - CountDownTimer<TIM>: CountDown<Time = Hertz>, + CountDownTimer<TIM>: CountDown<Time = MicroSecond>, { /// Starts timer in count down mode at a given frequency pub fn start_count_down<T>(self, timeout: T) -> CountDownTimer<TIM> where - T: Into<Hertz>, + T: Into<MicroSecond>, { let Self { tim, clk } = self; let mut timer = CountDownTimer { tim, clk }; @@ -87,14 +88,14 @@ impl CountDownTimer<SYST> { } } -impl CountDown for CountDownTimer<SYST> { - type Time = Hertz; +impl embedded_hal::timer::CountDown for CountDownTimer<SYST> { + type Time = MicroSecond; fn start<T>(&mut self, timeout: T) where - T: Into<Hertz>, + T: Into<MicroSecond>, { - let rvr = self.clk.0 / timeout.into().0 - 1; + let rvr = crate::time::cycles(timeout.into(), self.clk) - 1; assert!(rvr < (1 << 24)); @@ -112,6 +113,12 @@ impl CountDown for CountDownTimer<SYST> { } } +impl CountDown for CountDownTimer<SYST> { + fn max_period(&self) -> MicroSecond { + crate::time::duration(self.clk, (1 << 24) - 1) + } +} + impl Cancel for CountDownTimer<SYST> { type Error = Error; @@ -158,7 +165,7 @@ impl MonoTimer { /// Returns an `Instant` corresponding to "now" pub fn now(self) -> Instant { Instant { - now: DWT::get_cycle_count(), + now: DWT::cycle_count(), } } } @@ -172,7 +179,7 @@ pub struct Instant { impl Instant { /// Ticks elapsed since the `Instant` was created pub fn elapsed(self) -> u32 { - DWT::get_cycle_count().wrapping_sub(self.now) + DWT::cycle_count().wrapping_sub(self.now) } } @@ -249,24 +256,24 @@ macro_rules! hal { } } - impl CountDown for CountDownTimer<$TIM> { - type Time = Hertz; + impl embedded_hal::timer::CountDown for CountDownTimer<$TIM> { + type Time = MicroSecond; fn start<T>(&mut self, timeout: T) where - T: Into<Hertz>, + T: Into<MicroSecond>, { // pause self.tim.cr1.modify(|_, w| w.cen().clear_bit()); // reset counter self.tim.cnt.reset(); - let frequency = timeout.into().0; - let ticks = self.clk.0 / frequency; + let ticks = crate::time::cycles(timeout.into(), self.clk); let psc = u16((ticks - 1) / (1 << 16)).unwrap(); self.tim.psc.write(|w| unsafe {w.psc().bits(psc)} ); + // TODO: TIM2 and TIM5 are 32 bit let arr = u16(ticks / u32(psc + 1)).unwrap(); self.tim.arr.write(|w| unsafe { w.bits(u32(arr)) }); @@ -289,6 +296,13 @@ macro_rules! hal { } } + impl CountDown for CountDownTimer<$TIM> { + fn max_period(&self) -> MicroSecond { + // TODO: TIM2 and TIM5 are 32 bit + crate::time::duration(self.clk, u16::MAX as u32) + } + } + impl Cancel for CountDownTimer<$TIM> { type Error = Error;