//! API for the integrated timers //! //! This only implements basic functions, a lot of things are missing //! //! # Example //! Blink the led with 1Hz //! ``` no_run //! use stm32f0xx_hal as hal; //! //! use crate::hal::stm32; //! use crate::hal::prelude::*; //! use crate::hal::time::*; //! use crate::hal::timers::*; //! use nb::block; //! //! let mut p = stm32::Peripherals::take().unwrap(); //! //! let mut led = gpioa.pa1.into_push_pull_pull_output(); //! let rcc = p.RCC.constrain().cfgr.freeze(); //! let mut timer = Timer::tim1(p.TIM1, Hertz(1), clocks); //! loop { //! led.toggle(); //! block!(timer.wait()).ok(); //! } //! ``` use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; use crate::rcc::Clocks; use embedded_hal::timer::{CountDown, Periodic}; use nb; use void::Void; use crate::time::Hertz; /// Hardware timers pub struct Timer { clocks: Clocks, tim: TIM, } /// Interrupt events pub enum Event { /// Timer timed out / count down ended TimeOut, } impl Timer { /// Configures the SYST clock as a periodic count down timer pub fn syst(mut syst: SYST, timeout: T, clocks: Clocks) -> Self where T: Into, { syst.set_clock_source(SystClkSource::Core); let mut timer = Timer { tim: syst, clocks }; timer.start(timeout); timer } /// Starts listening for an `event` pub fn listen(&mut self, event: &Event) { match event { Event::TimeOut => self.tim.enable_interrupt(), } } /// Stops listening for an `event` pub fn unlisten(&mut self, event: &Event) { match event { Event::TimeOut => self.tim.disable_interrupt(), } } } /// Use the systick as a timer /// /// Be aware that intervals less than 4 Hertz may not function properly impl CountDown for Timer { type Time = Hertz; /// Start the timer with a `timeout` fn start(&mut self, timeout: T) where T: Into, { let rvr = self.clocks.sysclk().0 / timeout.into().0 - 1; assert!(rvr < (1 << 24)); self.tim.set_reload(rvr); self.tim.clear_current(); self.tim.enable_counter(); } /// Return `Ok` if the timer has wrapped /// Automatically clears the flag and restarts the time fn wait(&mut self) -> nb::Result<(), Void> { if self.tim.has_wrapped() { Ok(()) } else { Err(nb::Error::WouldBlock) } } } impl Periodic for Timer {} #[allow(unused)] macro_rules! timers { ($($TIM:ident: ($tim:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { $( use crate::stm32::$TIM; impl Timer<$TIM> { // XXX(why not name this `new`?) bummer: constructors need to have different names // even if the `$TIM` are non overlapping (compare to the `free` function below // which just works) /// Configures a TIM peripheral as a periodic count down timer pub fn $tim(tim: $TIM, timeout: T, clocks: Clocks) -> Self where T: Into, { // NOTE(unsafe) This executes only during initialisation let rcc = unsafe { &(*crate::stm32::RCC::ptr()) }; // enable and reset peripheral to a clean slate state rcc.$apbenr.modify(|_, w| w.$timXen().set_bit()); rcc.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); rcc.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); let mut timer = Timer { clocks, tim, }; timer.start(timeout); timer } /// Starts listening for an `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()); } } } /// Stops listening for an `event` pub fn unlisten(&mut self, event: Event) { match event { Event::TimeOut => { // Enable update event interrupt self.tim.dier.write(|w| w.uie().clear_bit()); } } } /// Releases the TIM peripheral pub fn release(self) -> $TIM { let rcc = unsafe { &(*crate::stm32::RCC::ptr()) }; // Pause counter self.tim.cr1.modify(|_, w| w.cen().clear_bit()); // Disable timer rcc.$apbenr.modify(|_, w| w.$timXen().clear_bit()); self.tim } } impl CountDown for Timer<$TIM> { type Time = Hertz; /// Start the timer with a `timeout` fn start(&mut self, timeout: T) where T: Into, { // pause self.tim.cr1.modify(|_, w| w.cen().clear_bit()); // restart counter self.tim.cnt.reset(); let frequency = timeout.into().0; let ticks = self.clocks.pclk().0 / frequency; let psc = cast::u16((ticks - 1) / (1 << 16)).unwrap(); self.tim.psc.write(|w| unsafe { w.psc().bits(psc) }); let arr = cast::u16(ticks / cast::u32(psc + 1)).unwrap(); self.tim.arr.write(|w| unsafe { w.bits(cast::u32(arr)) }); // start counter self.tim.cr1.modify(|_, w| w.cen().set_bit()); } /// Return `Ok` if the timer has wrapped /// Automatically clears the flag and restarts the time fn wait(&mut self) -> nb::Result<(), Void> { if self.tim.sr.read().uif().bit_is_clear() { Err(nb::Error::WouldBlock) } else { self.tim.sr.modify(|_, w| w.uif().clear_bit()); Ok(()) } } } impl Periodic for Timer<$TIM> {} )+ } } #[cfg(feature = "device-selected")] timers! { TIM1: (tim1, tim1en, tim1rst, apb2enr, apb2rstr), TIM3: (tim3, tim3en, tim3rst, apb1enr, apb1rstr), TIM14: (tim14, tim14en, tim14rst, apb1enr, apb1rstr), TIM16: (tim16, tim16en, tim16rst, apb2enr, apb2rstr), TIM17: (tim17, tim17en, tim17rst, apb2enr, apb2rstr), } #[cfg(any( feature = "stm32f030x8", feature = "stm32f030xc", feature = "stm32f070xb", feature = "stm32f072", feature = "stm32f091", ))] timers! { TIM6: (tim6, tim6en, tim6rst, apb1enr, apb1rstr), TIM15: (tim15, tim15en, tim15rst, apb2enr, apb2rstr), } #[cfg(any( feature = "stm32f030xc", feature = "stm32f070xb", feature = "stm32f072", feature = "stm32f091", ))] timers! { TIM7: (tim7, tim7en, tim7rst, apb1enr, apb1rstr), } #[cfg(any(feature = "stm32f042", feature = "stm32f072", feature = "stm32f091"))] timers! { TIM2: (tim2, tim2en, tim2rst, apb1enr, apb1rstr), }