You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
7.5 KiB
Rust
249 lines
7.5 KiB
Rust
//! 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<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, clocks: Clocks) -> Self
|
|
where
|
|
T: Into<Hertz>,
|
|
{
|
|
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<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> {}
|
|
|
|
#[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<T>(tim: $TIM, timeout: T, clocks: Clocks) -> Self
|
|
where
|
|
T: Into<Hertz>,
|
|
{
|
|
// 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<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> {}
|
|
)+
|
|
}
|
|
}
|
|
|
|
#[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),
|
|
}
|