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.
 
 
 
 

281 lines
8.4 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::pac;
//! use crate::hal::prelude::*;
//! use crate::hal::time::*;
//! use crate::hal::timers::*;
//! use nb::block;
//!
//! cortex_m::interrupt::free(|cs| {
//! let mut p = pac::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 core::convert::Infallible;
use crate::time::Hertz;
use embedded_hal::timer::{CountDown, Periodic};
/// 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.try_start(timeout).ok();
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;
type Error = Infallible;
/// Start the timer with a `timeout`
fn try_start<T>(&mut self, timeout: T) -> Result<(), Self::Error>
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();
Ok(())
}
/// Return `Ok` if the timer has wrapped
/// Automatically clears the flag and restarts the time
fn try_wait(&mut self) -> nb::Result<(), Self::Error> {
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::pac::$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.try_start(timeout).ok();
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::pac::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;
type Error = Infallible;
/// Start the timer with a `timeout`
fn try_start<T>(&mut self, timeout: T) -> Result<(), Self::Error>
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;
// If pclk is prescaled from hclk, the frequency fed into the timers is doubled
let tclk = if self.clocks.hclk().0 == self.clocks.pclk().0 {
self.clocks.pclk().0
} else {
self.clocks.pclk().0 * 2
};
let ticks = tclk / frequency;
let psc = cast::u16((ticks - 1) / (1 << 16)).unwrap();
self.tim.psc.write(|w| 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());
Ok(())
}
/// Return `Ok` if the timer has wrapped
/// Automatically clears the flag and restarts the time
fn try_wait(&mut self) -> nb::Result<(), Self::Error> {
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),
}