You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
7.9 KiB
270 lines
7.9 KiB
//! 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; |
|
//! |
|
//! cortex_m::interrupt::free(|cs| { |
|
//! let mut p = stm32::Peripherals::take().unwrap(); |
|
//! let mut rcc = p.RCC.configure().freeze(&mut p.FLASH); |
|
//! |
|
//! let gpioa = p.GPIOA.split(&mut rcc); |
|
//! |
|
//! let mut led = gpioa.pa1.into_push_pull_pull_output(cs); |
|
//! |
|
//! let mut timer = Timer::tim1(p.TIM1, Hertz(1), &mut rcc); |
|
//! loop { |
|
//! led.toggle(); |
|
//! block!(timer.wait()).ok(); |
|
//! } |
|
//! }); |
|
//! ``` |
|
use cortex_m::peripheral::syst::SystClkSource; |
|
use cortex_m::peripheral::SYST; |
|
|
|
use crate::rcc::{Clocks, Rcc}; |
|
|
|
use crate::time::Hertz; |
|
use embedded_hal::timer::{CountDown, Periodic}; |
|
use nb; |
|
use void::Void; |
|
|
|
/// Hardware timers |
|
pub struct Timer<TIM> { |
|
clocks: Clocks, |
|
tim: TIM, |
|
} |
|
|
|
/// Interrupt events |
|
pub enum Event { |
|
/// Timer timed out / count down ended |
|
TimeOut, |
|
} |
|
|
|
impl Timer<SYST> { |
|
/// Configures the SYST clock as a periodic count down timer |
|
pub fn syst<T>(mut syst: SYST, timeout: T, rcc: &Rcc) -> Self |
|
where |
|
T: Into<Hertz>, |
|
{ |
|
syst.set_clock_source(SystClkSource::Core); |
|
let mut timer = Timer { |
|
tim: syst, |
|
clocks: rcc.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<SYST> { |
|
type Time = Hertz; |
|
|
|
/// Start the timer with a `timeout` |
|
fn start<T>(&mut self, timeout: T) |
|
where |
|
T: Into<Hertz>, |
|
{ |
|
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<SYST> {} |
|
|
|
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<T>(tim: $TIM, timeout: T, rcc: &mut Rcc) -> Self |
|
where |
|
T: Into<Hertz>, |
|
{ |
|
// enable and reset peripheral to a clean slate state |
|
rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit()); |
|
rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); |
|
rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); |
|
|
|
let mut timer = Timer { |
|
clocks: rcc.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<T>(&mut self, timeout: T) |
|
where |
|
T: Into<Hertz>, |
|
{ |
|
// 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> {} |
|
)+ |
|
} |
|
} |
|
|
|
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 = "stm32f031", |
|
feature = "stm32f038", |
|
feature = "stm32f042", |
|
feature = "stm32f048", |
|
feature = "stm32f051", |
|
feature = "stm32f058", |
|
feature = "stm32f071", |
|
feature = "stm32f072", |
|
feature = "stm32f078", |
|
feature = "stm32f091", |
|
feature = "stm32f098", |
|
))] |
|
timers! { |
|
TIM2: (tim2, tim2en, tim2rst, apb1enr, apb1rstr), |
|
} |
|
|
|
#[cfg(any( |
|
feature = "stm32f030x8", |
|
feature = "stm32f030xc", |
|
feature = "stm32f051", |
|
feature = "stm32f058", |
|
feature = "stm32f070xb", |
|
feature = "stm32f071", |
|
feature = "stm32f072", |
|
feature = "stm32f078", |
|
feature = "stm32f091", |
|
feature = "stm32f098", |
|
))] |
|
timers! { |
|
TIM6: (tim6, tim6en, tim6rst, apb1enr, apb1rstr), |
|
TIM15: (tim15, tim15en, tim15rst, apb2enr, apb2rstr), |
|
} |
|
|
|
#[cfg(any( |
|
feature = "stm32f030xc", |
|
feature = "stm32f070xb", |
|
feature = "stm32f071", |
|
feature = "stm32f072", |
|
feature = "stm32f078", |
|
feature = "stm32f091", |
|
feature = "stm32f098", |
|
))] |
|
timers! { |
|
TIM7: (tim7, tim7en, tim7rst, apb1enr, apb1rstr), |
|
}
|
|
|