diff --git a/Cargo.toml b/Cargo.toml index 24c3e0c..76625c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,10 +60,14 @@ log-semihost = ["log"] cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] } stm32h5 = { package = "stm32h5", version = "0.16.0" } fugit = "0.3.7" +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal = "1.0.0" defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} +nb = "1.0.0" +void = { version = "1.0.2", default-features = false } +cast = { version = "0.3.0", default-features = false } [dev-dependencies] log = { version = "0.4.20"} @@ -89,6 +93,10 @@ opt-level = "s" # optimize for binary size [[example]] name = "blinky" +[[example]] +name = "blinky_timer" +required-features = ["stm32h533"] + [[example]] name = "i2c" required-features = ["stm32h503"] diff --git a/examples/blinky_timer.rs b/examples/blinky_timer.rs new file mode 100644 index 0000000..dd00db9 --- /dev/null +++ b/examples/blinky_timer.rs @@ -0,0 +1,84 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +mod utilities; + +use core::{ + cell::RefCell, + sync::atomic::{AtomicBool, Ordering}, +}; + +use cortex_m::interrupt::Mutex; +use cortex_m::peripheral::NVIC; +use cortex_m_rt::entry; + +use stm32h5xx_hal::gpio::gpioa::PA5; // LED pin +use stm32h5xx_hal::gpio::{Output, PushPull}; +use stm32h5xx_hal::{pac, pac::interrupt, prelude::*, timer}; +use utilities::logger::info; + +static LED_IS_ON: AtomicBool = AtomicBool::new(false); +static LED: Mutex>>>> = + Mutex::new(RefCell::new(None)); +static TIMER: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + utilities::logger::init(); + + let mut cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.vos0().freeze(); + + // Constrain and Freeze clock + let rcc = dp.RCC.constrain(); + let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + + let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); + let mut led = gpioa.pa5.into_push_pull_output(); + led.set_low(); + + let mut timer = dp.TIM2.timer(2.Hz(), ccdr.peripheral.TIM2, &ccdr.clocks); + timer.listen(timer::Event::TimeOut); + + cortex_m::interrupt::free(|cs| { + LED.borrow(cs).replace(Some(led)); + TIMER.borrow(cs).replace(Some(timer)); + }); + + info!("Start blinking with timer..."); + // Enable TIM2 interrupt + unsafe { + cp.NVIC.set_priority(interrupt::TIM2, 1); + NVIC::unmask::(interrupt::TIM2); + } + + loop { + // do_nothing + } +} + +/// Handle timer overflow +/// +/// The interrupt should be configured at maximum priority, it won't take very long. +#[interrupt] +fn TIM2() { + cortex_m::interrupt::free(|cs| { + if let Some(timer) = TIMER.borrow(cs).borrow_mut().as_mut() { + timer.clear_irq(); + } + // Signal that the interrupt fired + let led_is_on = LED_IS_ON.fetch_not(Ordering::Relaxed); + if let Some(led) = LED.borrow(cs).borrow_mut().as_mut() { + if led_is_on { + led.set_low(); + } else { + led.set_high(); + } + } + }) +} diff --git a/src/lib.rs b/src/lib.rs index ece8ce4..9a6dd1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,9 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![allow(non_camel_case_types)] +pub use nb; +pub use nb::block; + #[cfg(not(feature = "device-selected"))] compile_error!( "This crate requires one of the following device features enabled: @@ -73,6 +76,9 @@ pub mod icache; #[cfg(feature = "device-selected")] pub mod delay; +#[cfg(feature = "device-selected")] +pub mod timer; + #[cfg(feature = "device-selected")] mod sealed { pub trait Sealed {} diff --git a/src/prelude.rs b/src/prelude.rs index d0d2d40..d7ee70d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,4 +1,5 @@ //! Prelude +pub use embedded_hal_02::prelude::*; pub use crate::delay::DelayExt as _stm32h5xx_hal_delay_DelayExt; pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt; @@ -6,6 +7,7 @@ pub use crate::i2c::I2cExt as _stm32h5xx_hal_i2c_I2cExt; pub use crate::icache::ICacheExt as _stm32h5xx_hal_icache_ICacheExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt; +pub use crate::timer::TimerExt as _stm32h5xx_hal_timer_TimerExt; pub use crate::time::U32Ext as _; pub use fugit::{ExtU32 as _, RateExtU32 as _}; diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..f56fe1e --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,441 @@ +//! Timers +//! +//! # Examples +//! +//! - [Blinky using a Timer](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/blinky_timer.rs) +//! - [64 bit microsecond timer](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/tick_timer.rs) + +// TODO: on the h7x3 at least, only TIM2, TIM3, TIM4, TIM5 can support 32 bits. +// TIM1 is 16 bit. + +use core::marker::PhantomData; + +use crate::stm32::{TIM1, TIM2, TIM3, TIM6, TIM7}; +#[cfg(feature = "rm0481")] +use crate::stm32::{TIM12, TIM15, TIM4, TIM5, TIM8}; + +use cast::{u16, u32}; +use void::Void; + +use crate::rcc::{rec, CoreClocks, ResetEnable}; +use crate::time::Hertz; + +/// Associate clocks with timers +pub trait GetClk { + fn get_clk(clocks: &CoreClocks) -> Option; +} + +/// Timers with CK_INT derived from rcc_tim[xy]_ker_ck +macro_rules! impl_tim_ker_ck { + ($($ckX:ident: $($TIMX:ident),+)+) => { + $( + $( + impl GetClk for $TIMX { + fn get_clk(clocks: &CoreClocks) -> Option { + Some(clocks.$ckX()) + } + } + )+ + )+ + } +} +impl_tim_ker_ck! { + timx_ker_ck: TIM2, TIM3, TIM6, TIM7 + timy_ker_ck: TIM1 +} + +#[cfg(feature = "rm0481")] +impl_tim_ker_ck! { + timx_ker_ck: TIM4, TIM5, TIM12 + timy_ker_ck: TIM8, TIM15 +} + +/// External trait for hardware timers +pub trait TimerExt { + type Rec: ResetEnable; + + /// Configures a periodic timer + /// + /// Generates an overflow event at the `timeout` frequency. + fn timer(self, timeout: Hertz, prec: Self::Rec, clocks: &CoreClocks) + -> TIM; + + /// Configures the timer to count up at the given frequency + /// + /// Counts from 0 to the counter's maximum value, then repeats. + /// Because this only uses the timer prescaler, the frequency + /// is rounded to a multiple of the timer's kernel clock. + /// + /// For example, calling `.tick_timer(1.MHz(), ..)` for a 16-bit timer will + /// result in a timers that increments every microsecond and overflows every + /// ~65 milliseconds + fn tick_timer( + self, + frequency: Hertz, + prec: Self::Rec, + clocks: &CoreClocks, + ) -> TIM; +} + +/// Hardware timers +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Timer { + clk: u32, + tim: TIM, +} + +/// Timer Events +/// +/// Each event is a possible interrupt source, if enabled +pub enum Event { + /// Timer timed out / count down ended + TimeOut, +} + +macro_rules! hal { + ($($TIMX:ident: ($timX:ident, $Rec:ident, $cntType:ty),)+) => { + $( + impl embedded_hal_02::timer::Periodic for Timer<$TIMX> {} + + impl embedded_hal_02::timer::CountDown for Timer<$TIMX> { + type Time = Hertz; + + fn start(&mut self, timeout: T) + where + T: Into, + { + // Pause + self.pause(); + + // Reset counter + self.reset_counter(); + + // UEV event occours on next overflow + self.urs_counter_only(); + self.clear_irq(); + + // Set PSC and ARR + self.set_freq(timeout.into()); + + // Generate an update event to force an update of the ARR register. This ensures + // the first timer cycle is of the specified duration. + self.apply_freq(); + + // Start counter + self.resume() + } + + fn wait(&mut self) -> nb::Result<(), Void> { + if self.is_irq_clear() { + Err(nb::Error::WouldBlock) + } else { + self.clear_irq(); + Ok(()) + } + } + } + + impl TimerExt> for $TIMX { + type Rec = rec::$Rec; + + fn timer(self, timeout: Hertz, + prec: Self::Rec, clocks: &CoreClocks + ) -> Timer<$TIMX> { + use embedded_hal_02::timer::CountDown; + + let mut timer = Timer::$timX(self, prec, clocks); + timer.start(timeout); + timer + } + + fn tick_timer(self, frequency: Hertz, + prec: Self::Rec, clocks: &CoreClocks + ) -> Timer<$TIMX> { + let mut timer = Timer::$timX(self, prec, clocks); + + timer.pause(); + + // UEV event occours on next overflow + timer.urs_counter_only(); + timer.clear_irq(); + + // Set PSC and ARR + timer.set_tick_freq(frequency); + + // Generate an update event to force an update of the ARR + // register. This ensures the first timer cycle is of the + // specified duration. + timer.apply_freq(); + + // Start counter + timer.resume(); + + timer + } + } + + impl Timer<$TIMX> { + /// Configures a TIM peripheral as a periodic count down timer, + /// without starting it + pub fn $timX(tim: $TIMX, prec: rec::$Rec, clocks: &CoreClocks) -> Self + { + // enable and reset peripheral to a clean state + let _ = prec.enable().reset(); // drop, can be recreated by free method + + let clk = $TIMX::get_clk(clocks) + .expect(concat!(stringify!($TIMX), ": Input clock not running!")).raw(); + + Timer { + clk, + tim, + } + } + + /// Configures the timer's frequency and counter reload value + /// so that it underflows at the timeout's frequency + pub fn set_freq(&mut self, timeout: Hertz) { + let ticks = self.clk / timeout.raw(); + + self.set_timeout_ticks(ticks); + } + + /// Sets the timer period from a time duration + /// + /// ``` + /// use stm32h7xx_hal::time::MilliSeconds; + /// + /// // Set timeout to 100ms + /// let timeout = MilliSeconds::from_ticks(100).into_rate(); + /// timer.set_timeout(timeout); + /// ``` + /// + /// Alternatively, the duration can be set using the + /// core::time::Duration type + /// + /// ``` + /// let duration = core::time::Duration::from_nanos(2_500); + /// + /// // Set timeout to 2.5µs + /// timer.set_timeout(duration); + /// ``` + pub fn set_timeout(&mut self, timeout: T) + where + T: Into + { + const NANOS_PER_SECOND: u64 = 1_000_000_000; + let timeout = timeout.into(); + + let clk = self.clk as u64; + let ticks = u32::try_from( + clk * timeout.as_secs() + + clk * u64::from(timeout.subsec_nanos()) / NANOS_PER_SECOND, + ) + .unwrap_or(u32::MAX); + + self.set_timeout_ticks(ticks.max(1)); + } + + /// Sets the timer's prescaler and auto reload register so that the timer will reach + /// the ARR after `ticks - 1` amount of timer clock ticks. + /// + /// ``` + /// // Set auto reload register to 50000 and prescaler to divide by 2. + /// timer.set_timeout_ticks(100000); + /// ``` + /// + /// This function will round down if the prescaler is used to extend the range: + /// ``` + /// // Set auto reload register to 50000 and prescaler to divide by 2. + /// timer.set_timeout_ticks(100001); + /// ``` + fn set_timeout_ticks(&mut self, ticks: u32) { + let (psc, arr) = calculate_timeout_ticks_register_values(ticks); + unsafe { + self.tim.psc().write(|w| w.psc().bits(psc)); + } + #[allow(unused_unsafe)] // method is safe for some timers + self.tim.arr().write(|w| unsafe { w.bits(u32(arr)) }); + } + + /// Configures the timer to count up at the given frequency + /// + /// Counts from 0 to the counter's maximum value, then repeats. + /// Because this only uses the timer prescaler, the frequency + /// is rounded to a multiple of the timer's kernel clock. + pub fn set_tick_freq(&mut self, frequency: Hertz) { + let div = self.clk / frequency.raw(); + + let psc = u16(div - 1).unwrap(); + unsafe { + self.tim.psc().write(|w| w.psc().bits(psc)); + } + + let counter_max = u32(<$cntType>::MAX); + #[allow(unused_unsafe)] // method is safe for some timers + self.tim.arr().write(|w| unsafe { w.bits(counter_max) }); + } + + /// Applies frequency/timeout changes immediately + /// + /// The timer will normally update its prescaler and auto-reload + /// value when its counter overflows. This function causes + /// those changes to happen immediately. Also clears the counter. + pub fn apply_freq(&mut self) { + self.tim.egr().write(|w| w.ug().set_bit()); + } + + /// Pauses the TIM peripheral + pub fn pause(&mut self) { + self.tim.cr1().modify(|_, w| w.cen().clear_bit()); + } + + /// Resume (unpause) the TIM peripheral + pub fn resume(&mut self) { + self.tim.cr1().modify(|_, w| w.cen().set_bit()); + } + + /// Set Update Request Source to counter overflow/underflow only + pub fn urs_counter_only(&mut self) { + self.tim.cr1().modify(|_, w| w.urs().counter_only()); + } + + /// Reset the counter of the TIM peripheral + pub fn reset_counter(&mut self) { + self.tim.cnt().reset(); + } + + /// Read the counter of the TIM peripheral + pub fn counter(&self) -> u32 { + self.tim.cnt().read().cnt().bits().into() + } + + /// Start listening for `event` + pub fn listen(&mut self, event: Event) { + match event { + Event::TimeOut => { + // Enable update event interrupt + self.tim.dier().write(|w| w.uie().set_bit()); + } + } + } + + /// Stop listening for `event` + pub fn unlisten(&mut self, event: Event) { + match event { + Event::TimeOut => { + // Disable update event interrupt + self.tim.dier().write(|w| w.uie().clear_bit()); + let _ = self.tim.dier().read(); + let _ = self.tim.dier().read(); // Delay 2 peripheral clocks + } + } + } + + /// Check if Update Interrupt flag is cleared + pub fn is_irq_clear(&mut self) -> bool { + self.tim.sr().read().uif().bit_is_clear() + } + + /// Clears interrupt flag + pub fn clear_irq(&mut self) { + self.tim.sr().modify(|_, w| { + // Clears timeout event + w.uif().clear_bit() + }); + let _ = self.tim.sr().read(); + let _ = self.tim.sr().read(); // Delay 2 peripheral clocks + } + + /// Releases the TIM peripheral + pub fn free(mut self) -> ($TIMX, rec::$Rec) { + // pause counter + self.pause(); + + (self.tim, rec::$Rec { _marker: PhantomData }) + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &$TIMX { + &self.tim + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut $TIMX { + &mut self.tim + } + } + )+ + } +} + +/// We want to have `ticks` amount of timer ticks before it reloads. +/// But `ticks` may have a higher value than what the timer can hold directly. +/// So we'll use the prescaler to extend the range. +/// +/// To know how many times we would overflow with a prescaler of 1, we divide `ticks` by 2^16 (the max amount of ticks per overflow). +/// If the result is e.g. 3, then we need to increase our range by 4 times to fit all the ticks. +/// We can increase the range enough by setting the prescaler to 3 (which will divide the clock freq by 4). +/// Because every tick is now 4x as long, we need to divide `ticks` by 4 to keep the same timeout. +/// +/// This function returns the prescaler register value and auto reload register value. +fn calculate_timeout_ticks_register_values(ticks: u32) -> (u16, u16) { + // Note (unwrap): Never panics because 32-bit value is shifted right by 16 bits, + // resulting in a value that always fits in 16 bits. + let psc = u16(ticks / (1 << 16)).unwrap(); + // Note (unwrap): Never panics because the divisor is always such that the result fits in 16 bits. + // Also note that the timer counts `0..=arr`, so subtract 1 to get the correct period. + let arr = u16(ticks / (u32(psc) + 1)).unwrap().saturating_sub(1); + (psc, arr) +} + +hal! { + // Advanced-control + TIM1: (tim1, Tim1, u16), + + // General-purpose + TIM2: (tim2, Tim2, u32), + TIM3: (tim3, Tim3, u16), + + // Basic + TIM6: (tim6, Tim6, u16), + TIM7: (tim7, Tim7, u16), +} + +#[cfg(feature = "rm0481")] +hal! { + // Advanced-control + TIM8: (tim8, Tim8, u16), + + // General-purpose + TIM4: (tim4, Tim4, u16), + TIM5: (tim5, Tim5, u32), + + // General-purpose + TIM12: (tim12, Tim12, u16), + + // General-purpose + TIM15: (tim15, Tim15, u16), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn timeout_ticks_register_values() { + assert_eq!(calculate_timeout_ticks_register_values(0), (0, 0)); + assert_eq!(calculate_timeout_ticks_register_values(50000), (0, 49999)); + assert_eq!(calculate_timeout_ticks_register_values(100000), (1, 49999)); + assert_eq!(calculate_timeout_ticks_register_values(65535), (0, 65534)); + assert_eq!(calculate_timeout_ticks_register_values(65536), (1, 32767)); + assert_eq!( + calculate_timeout_ticks_register_values(1000000), + (15, 62499) + ); + assert_eq!( + calculate_timeout_ticks_register_values(u32::MAX), + (u16::MAX, u16::MAX - 1) + ); + } +}