2021-11-09 18:30:46 +01:00
|
|
|
//! API for the TIM peripherals
|
|
|
|
//!
|
|
|
|
//! ## Examples
|
|
|
|
//!
|
|
|
|
//! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs)
|
2021-11-08 01:40:01 +01:00
|
|
|
use crate::{
|
|
|
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
2021-12-05 17:28:30 +01:00
|
|
|
gpio::{
|
|
|
|
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
|
|
|
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
|
|
|
|
PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21,
|
|
|
|
PB22, PB23, PB3, PB4, PB5, PB6,
|
|
|
|
},
|
2021-12-05 22:57:54 +01:00
|
|
|
pac::{
|
|
|
|
self, tim0, TIM0, TIM1, TIM10, TIM11, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17, TIM18,
|
|
|
|
TIM19, TIM2, TIM20, TIM21, TIM22, TIM23, TIM3, TIM4, TIM5, TIM6, TIM7, TIM8, TIM9,
|
|
|
|
},
|
2021-11-13 14:43:21 +01:00
|
|
|
prelude::*,
|
2021-12-05 17:28:30 +01:00
|
|
|
private::Sealed,
|
2021-11-08 01:40:01 +01:00
|
|
|
time::Hertz,
|
2021-11-13 14:43:21 +01:00
|
|
|
timer,
|
2021-11-08 01:40:01 +01:00
|
|
|
};
|
2021-11-17 10:34:55 +01:00
|
|
|
use core::cell::Cell;
|
|
|
|
use cortex_m::interrupt::Mutex;
|
|
|
|
use embedded_hal::{
|
|
|
|
blocking::delay,
|
|
|
|
timer::{Cancel, CountDown, Periodic},
|
|
|
|
};
|
2021-11-08 01:40:01 +01:00
|
|
|
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
|
|
|
|
use void::Void;
|
|
|
|
|
|
|
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
2021-11-17 10:34:55 +01:00
|
|
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
2021-11-08 01:40:01 +01:00
|
|
|
|
2021-12-05 17:28:30 +01:00
|
|
|
//==================================================================================================
|
|
|
|
// Defintions
|
|
|
|
//==================================================================================================
|
2021-11-08 01:40:01 +01:00
|
|
|
|
|
|
|
/// Interrupt events
|
|
|
|
pub enum Event {
|
|
|
|
/// Timer timed out / count down ended
|
|
|
|
TimeOut,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum TimerErrors {
|
|
|
|
Canceled,
|
|
|
|
}
|
|
|
|
|
2021-12-05 17:28:30 +01:00
|
|
|
//==================================================================================================
|
|
|
|
// Valid TIM and PIN combinations
|
|
|
|
//==================================================================================================
|
|
|
|
|
|
|
|
pub trait TimPin {
|
|
|
|
const DYN: DynPinId;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait ValidTim {
|
|
|
|
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
|
|
|
const TIM_ID: u8;
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! tim_marker {
|
|
|
|
($TIMX:ident, $ID:expr) => {
|
|
|
|
impl ValidTim for $TIMX {
|
|
|
|
const TIM_ID: u8 = $ID;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
tim_marker!(TIM0, 0);
|
|
|
|
tim_marker!(TIM1, 1);
|
|
|
|
tim_marker!(TIM2, 2);
|
|
|
|
tim_marker!(TIM3, 3);
|
|
|
|
tim_marker!(TIM4, 4);
|
|
|
|
tim_marker!(TIM5, 5);
|
|
|
|
tim_marker!(TIM6, 6);
|
|
|
|
tim_marker!(TIM7, 7);
|
|
|
|
tim_marker!(TIM8, 8);
|
|
|
|
tim_marker!(TIM9, 9);
|
|
|
|
tim_marker!(TIM10, 10);
|
|
|
|
tim_marker!(TIM11, 11);
|
|
|
|
tim_marker!(TIM12, 12);
|
|
|
|
tim_marker!(TIM13, 13);
|
|
|
|
tim_marker!(TIM14, 14);
|
|
|
|
tim_marker!(TIM15, 15);
|
|
|
|
tim_marker!(TIM16, 16);
|
|
|
|
tim_marker!(TIM17, 17);
|
|
|
|
tim_marker!(TIM18, 18);
|
|
|
|
tim_marker!(TIM19, 19);
|
|
|
|
tim_marker!(TIM20, 20);
|
|
|
|
tim_marker!(TIM21, 21);
|
|
|
|
tim_marker!(TIM22, 22);
|
|
|
|
tim_marker!(TIM23, 23);
|
|
|
|
|
|
|
|
pub trait ValidTimAndPin<PIN: TimPin, TIM: ValidTim>: Sealed {}
|
|
|
|
|
|
|
|
macro_rules! pin_and_tim {
|
|
|
|
($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:ident) => {
|
|
|
|
impl TimPin for Pin<$PAX, $ALTFUNC>
|
|
|
|
where
|
|
|
|
$PAX: PinId,
|
|
|
|
{
|
|
|
|
const DYN: DynPinId = $PAX::DYN;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> for (Pin<$PAX, $ALTFUNC>, $TIMX)
|
|
|
|
where
|
|
|
|
Pin<$PAX, $ALTFUNC>: TimPin,
|
|
|
|
$PAX: PinId,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pin_and_tim!(PA31, AltFunc2, 23, TIM23);
|
|
|
|
pin_and_tim!(PA30, AltFunc2, 22, TIM22);
|
|
|
|
pin_and_tim!(PA29, AltFunc2, 21, TIM21);
|
|
|
|
pin_and_tim!(PA28, AltFunc2, 20, TIM20);
|
|
|
|
pin_and_tim!(PA27, AltFunc2, 19, TIM19);
|
|
|
|
pin_and_tim!(PA26, AltFunc2, 18, TIM18);
|
|
|
|
pin_and_tim!(PA25, AltFunc2, 17, TIM17);
|
|
|
|
pin_and_tim!(PA24, AltFunc2, 16, TIM16);
|
|
|
|
|
|
|
|
pin_and_tim!(PA15, AltFunc1, 15, TIM15);
|
|
|
|
pin_and_tim!(PA14, AltFunc1, 14, TIM14);
|
|
|
|
pin_and_tim!(PA13, AltFunc1, 13, TIM13);
|
|
|
|
pin_and_tim!(PA12, AltFunc1, 12, TIM12);
|
|
|
|
pin_and_tim!(PA11, AltFunc1, 11, TIM11);
|
|
|
|
pin_and_tim!(PA10, AltFunc1, 10, TIM10);
|
|
|
|
pin_and_tim!(PA9, AltFunc1, 9, TIM9);
|
|
|
|
pin_and_tim!(PA8, AltFunc1, 8, TIM8);
|
|
|
|
pin_and_tim!(PA7, AltFunc1, 7, TIM7);
|
|
|
|
pin_and_tim!(PA6, AltFunc1, 6, TIM6);
|
|
|
|
pin_and_tim!(PA5, AltFunc1, 5, TIM5);
|
|
|
|
pin_and_tim!(PA4, AltFunc1, 4, TIM4);
|
|
|
|
pin_and_tim!(PA3, AltFunc1, 3, TIM3);
|
|
|
|
pin_and_tim!(PA2, AltFunc1, 2, TIM2);
|
|
|
|
pin_and_tim!(PA1, AltFunc1, 1, TIM1);
|
|
|
|
pin_and_tim!(PA0, AltFunc1, 0, TIM0);
|
|
|
|
|
|
|
|
pin_and_tim!(PB23, AltFunc3, 23, TIM23);
|
|
|
|
pin_and_tim!(PB22, AltFunc3, 22, TIM22);
|
|
|
|
pin_and_tim!(PB21, AltFunc3, 21, TIM21);
|
|
|
|
pin_and_tim!(PB20, AltFunc3, 20, TIM20);
|
|
|
|
pin_and_tim!(PB19, AltFunc3, 19, TIM19);
|
|
|
|
pin_and_tim!(PB18, AltFunc3, 18, TIM18);
|
|
|
|
pin_and_tim!(PB17, AltFunc3, 17, TIM17);
|
|
|
|
pin_and_tim!(PB16, AltFunc3, 16, TIM16);
|
|
|
|
pin_and_tim!(PB15, AltFunc3, 15, TIM15);
|
|
|
|
pin_and_tim!(PB14, AltFunc3, 14, TIM14);
|
|
|
|
pin_and_tim!(PB13, AltFunc3, 13, TIM13);
|
|
|
|
pin_and_tim!(PB12, AltFunc3, 12, TIM12);
|
|
|
|
pin_and_tim!(PB11, AltFunc3, 11, TIM11);
|
|
|
|
pin_and_tim!(PB10, AltFunc3, 10, TIM10);
|
|
|
|
|
|
|
|
pin_and_tim!(PB6, AltFunc3, 6, TIM6);
|
|
|
|
pin_and_tim!(PB5, AltFunc3, 5, TIM5);
|
|
|
|
pin_and_tim!(PB4, AltFunc3, 4, TIM4);
|
|
|
|
pin_and_tim!(PB3, AltFunc3, 3, TIM3);
|
|
|
|
pin_and_tim!(PB2, AltFunc3, 2, TIM2);
|
|
|
|
pin_and_tim!(PB1, AltFunc3, 1, TIM1);
|
|
|
|
pin_and_tim!(PB0, AltFunc3, 0, TIM0);
|
|
|
|
|
|
|
|
//==================================================================================================
|
2021-12-05 22:57:54 +01:00
|
|
|
// Register Interface for TIM registers and TIM pins
|
2021-12-05 17:28:30 +01:00
|
|
|
//==================================================================================================
|
|
|
|
|
|
|
|
pub type TimRegBlock = tim0::RegisterBlock;
|
|
|
|
|
|
|
|
/// Register interface.
|
|
|
|
///
|
|
|
|
/// This interface provides valid TIM pins a way to access their corresponding TIM
|
|
|
|
/// registers
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
2021-12-05 22:57:54 +01:00
|
|
|
/// Users should only implement the [`tim_id`] function. No default function
|
2021-12-05 17:28:30 +01:00
|
|
|
/// implementations should be overridden. The implementing type must also have
|
|
|
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
|
|
|
/// pin ID is a singleton.
|
|
|
|
pub(super) unsafe trait TimRegInterface {
|
|
|
|
fn tim_id(&self) -> u8;
|
|
|
|
|
|
|
|
const PORT_BASE: *const tim0::RegisterBlock = TIM0::ptr() as *const _;
|
|
|
|
|
|
|
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
|
|
|
/// memory mapped peripheral depending on the TIM ID.
|
|
|
|
#[inline(always)]
|
2021-12-05 22:57:54 +01:00
|
|
|
fn reg(&self) -> &TimRegBlock {
|
2021-12-05 17:28:30 +01:00
|
|
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn mask_32(&self) -> u32 {
|
|
|
|
1 << self.tim_id()
|
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
|
|
|
|
/// Clear the reset bit of the TIM, holding it in reset
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
|
|
|
#[inline]
|
|
|
|
fn clear_tim_reset_bit(&self) {
|
|
|
|
unsafe {
|
|
|
|
va108xx::Peripherals::steal()
|
|
|
|
.SYSCONFIG
|
|
|
|
.tim_reset
|
|
|
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_tim_reset_bit(&self) {
|
|
|
|
unsafe {
|
|
|
|
va108xx::Peripherals::steal()
|
|
|
|
.SYSCONFIG
|
|
|
|
.tim_reset
|
|
|
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Register interface.
|
|
|
|
///
|
|
|
|
/// This interface provides an interface for TIM pins to access their corresponding
|
|
|
|
/// configuration
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// Users should only implement the [`pin_id`] function. No default function
|
|
|
|
/// implementations should be overridden. The implementing type must also have
|
|
|
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
|
|
|
/// pin ID is a singleton.
|
|
|
|
pub(super) unsafe trait TimPinInterface {
|
|
|
|
fn pin_id(&self) -> DynPinId;
|
2021-12-05 17:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
|
|
|
///
|
|
|
|
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
|
|
|
|
/// access the corresponding registers.
|
2021-12-05 22:57:54 +01:00
|
|
|
pub(super) struct TimAndPinRegister<PIN: TimPin, TIM: ValidTim> {
|
2021-12-05 17:28:30 +01:00
|
|
|
pin: PIN,
|
|
|
|
tim: TIM,
|
|
|
|
}
|
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
pub(super) struct TimRegister<TIM: ValidTim> {
|
|
|
|
tim: TIM,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<TIM: ValidTim> TimRegister<TIM> {
|
|
|
|
#[inline]
|
|
|
|
pub(super) unsafe fn new(tim: TIM) -> Self {
|
|
|
|
TimRegister { tim }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn release(self) -> TIM {
|
|
|
|
self.tim
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<TIM: ValidTim> TimRegInterface for TimRegister<TIM> {
|
|
|
|
fn tim_id(&self) -> u8 {
|
|
|
|
TIM::TIM_ID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM>
|
2021-12-05 17:28:30 +01:00
|
|
|
where
|
|
|
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self {
|
2021-12-05 22:57:54 +01:00
|
|
|
TimAndPinRegister { pin, tim }
|
2021-12-05 17:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn release(self) -> (PIN, TIM) {
|
|
|
|
(self.pin, self.tim)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
unsafe impl<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimAndPinRegister<PIN, TIM> {
|
2021-12-05 17:28:30 +01:00
|
|
|
#[inline(always)]
|
|
|
|
fn tim_id(&self) -> u8 {
|
|
|
|
TIM::TIM_ID
|
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
}
|
2021-12-05 17:28:30 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
unsafe impl<PIN: TimPin, TIM: ValidTim> TimPinInterface for TimAndPinRegister<PIN, TIM> {
|
2021-12-05 17:28:30 +01:00
|
|
|
#[inline(always)]
|
|
|
|
fn pin_id(&self) -> DynPinId {
|
|
|
|
PIN::DYN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) struct TimDynRegister {
|
|
|
|
tim_id: u8,
|
|
|
|
pin_id: DynPinId,
|
|
|
|
}
|
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
impl<PIN: TimPin, TIM: ValidTim> From<TimAndPinRegister<PIN, TIM>> for TimDynRegister {
|
|
|
|
fn from(_reg: TimAndPinRegister<PIN, TIM>) -> Self {
|
2021-12-05 17:28:30 +01:00
|
|
|
Self {
|
|
|
|
tim_id: TIM::TIM_ID,
|
|
|
|
pin_id: PIN::DYN,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl TimRegInterface for TimDynRegister {
|
|
|
|
#[inline(always)]
|
|
|
|
fn tim_id(&self) -> u8 {
|
|
|
|
self.tim_id
|
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
}
|
2021-12-05 17:28:30 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
unsafe impl TimPinInterface for TimDynRegister {
|
2021-12-05 17:28:30 +01:00
|
|
|
#[inline(always)]
|
|
|
|
fn pin_id(&self) -> DynPinId {
|
|
|
|
self.pin_id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==================================================================================================
|
|
|
|
// Timers
|
|
|
|
//==================================================================================================
|
|
|
|
|
|
|
|
/// Hardware timers
|
2021-12-05 22:57:54 +01:00
|
|
|
pub struct CountDownTimer<TIM: ValidTim> {
|
|
|
|
tim: TimRegister<TIM>,
|
2021-12-05 17:28:30 +01:00
|
|
|
curr_freq: Hertz,
|
|
|
|
sys_clk: Hertz,
|
|
|
|
rst_val: u32,
|
|
|
|
last_cnt: u32,
|
|
|
|
listening: bool,
|
|
|
|
}
|
|
|
|
|
2021-11-08 01:40:01 +01:00
|
|
|
fn enable_tim_clk(syscfg: &mut SYSCONFIG, idx: u8) {
|
|
|
|
syscfg
|
|
|
|
.tim_clk_enable
|
|
|
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
|
|
|
}
|
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
unsafe impl<TIM: ValidTim> TimRegInterface for CountDownTimer<TIM> {
|
|
|
|
fn tim_id(&self) -> u8 {
|
|
|
|
TIM::TIM_ID
|
|
|
|
}
|
|
|
|
}
|
2021-11-08 01:40:01 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
impl<TIM: ValidTim> CountDownTimer<TIM> {
|
|
|
|
/// Configures a TIM peripheral as a periodic count down timer
|
|
|
|
pub fn new(syscfg: &mut SYSCONFIG, sys_clk: Hertz, tim: TIM) -> Self {
|
|
|
|
enable_tim_clk(syscfg, TIM::TIM_ID);
|
|
|
|
let cd_timer = CountDownTimer {
|
|
|
|
tim: unsafe { TimRegister::new(tim) },
|
|
|
|
sys_clk,
|
|
|
|
rst_val: 0,
|
|
|
|
curr_freq: 0.hz(),
|
|
|
|
listening: false,
|
|
|
|
last_cnt: 0,
|
|
|
|
};
|
|
|
|
cd_timer.tim.reg().ctrl.modify(|_, w| w.enable().set_bit());
|
|
|
|
cd_timer
|
|
|
|
}
|
2021-11-08 01:40:01 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
/// Listen for events. This also actives the IRQ in the IRQSEL register
|
|
|
|
/// for the provided interrupt. It also actives the peripheral clock for
|
|
|
|
/// IRQSEL
|
|
|
|
pub fn listen(
|
|
|
|
&mut self,
|
|
|
|
event: Event,
|
|
|
|
syscfg: &mut SYSCONFIG,
|
|
|
|
irqsel: &mut IRQSEL,
|
|
|
|
interrupt: Interrupt,
|
|
|
|
) {
|
|
|
|
match event {
|
|
|
|
Event::TimeOut => {
|
|
|
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
|
|
|
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(interrupt as u32) });
|
|
|
|
self.tim.reg().ctrl.modify(|_, w| w.irq_enb().set_bit());
|
|
|
|
self.listening = true;
|
2021-11-08 01:40:01 +01:00
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-17 10:34:55 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
pub fn unlisten(&mut self, event: Event, syscfg: &mut SYSCONFIG, irqsel: &mut IRQSEL) {
|
|
|
|
match event {
|
|
|
|
Event::TimeOut => {
|
|
|
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
|
|
|
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
|
|
|
self.tim.reg().ctrl.modify(|_, w| w.irq_enb().clear_bit());
|
|
|
|
self.listening = false;
|
2021-11-17 10:34:55 +01:00
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-17 10:34:55 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
pub fn release(self, syscfg: &mut SYSCONFIG) -> TIM {
|
|
|
|
self.tim.reg().ctrl.write(|w| w.enable().clear_bit());
|
|
|
|
syscfg
|
|
|
|
.tim_clk_enable
|
|
|
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << TIM::TIM_ID)) });
|
|
|
|
self.tim.release()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn auto_disable(self, enable: bool) -> Self {
|
|
|
|
if enable {
|
|
|
|
self.tim
|
|
|
|
.reg()
|
|
|
|
.ctrl
|
|
|
|
.modify(|_, w| w.auto_disable().set_bit());
|
|
|
|
} else {
|
|
|
|
self.tim
|
|
|
|
.reg()
|
|
|
|
.ctrl
|
|
|
|
.modify(|_, w| w.auto_disable().clear_bit());
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn auto_deactivate(self, enable: bool) -> Self {
|
|
|
|
if enable {
|
|
|
|
self.tim
|
|
|
|
.reg()
|
|
|
|
.ctrl
|
|
|
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
|
|
|
} else {
|
|
|
|
self.tim
|
|
|
|
.reg()
|
|
|
|
.ctrl
|
|
|
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn curr_freq(&self) -> Hertz {
|
|
|
|
self.curr_freq
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn listening(&self) -> bool {
|
|
|
|
self.listening
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CountDown implementation for TIMx
|
|
|
|
impl<TIM: ValidTim> CountDown for CountDownTimer<TIM> {
|
|
|
|
type Time = Hertz;
|
|
|
|
|
|
|
|
fn start<T>(&mut self, timeout: T)
|
|
|
|
where
|
|
|
|
T: Into<Hertz>,
|
|
|
|
{
|
|
|
|
self.tim.reg().ctrl.modify(|_, w| w.enable().clear_bit());
|
|
|
|
self.curr_freq = timeout.into();
|
|
|
|
self.rst_val = self.sys_clk.0 / self.curr_freq.0;
|
|
|
|
unsafe {
|
|
|
|
self.tim.reg().rst_value.write(|w| w.bits(self.rst_val));
|
|
|
|
self.tim.reg().cnt_value.write(|w| w.bits(self.rst_val));
|
|
|
|
}
|
|
|
|
self.tim.reg().ctrl.modify(|_, w| w.enable().set_bit());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
|
|
|
/// flag and restart the time if configured correctly
|
|
|
|
fn wait(&mut self) -> nb::Result<(), Void> {
|
|
|
|
let cnt = self.tim.reg().cnt_value.read().bits();
|
|
|
|
if (cnt > self.last_cnt) || cnt == 0 {
|
|
|
|
self.last_cnt = self.rst_val;
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
self.last_cnt = cnt;
|
|
|
|
Err(nb::Error::WouldBlock)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<TIM: ValidTim> Periodic for CountDownTimer<TIM> {}
|
|
|
|
|
|
|
|
impl<TIM: ValidTim> Cancel for CountDownTimer<TIM> {
|
|
|
|
type Error = TimerErrors;
|
|
|
|
fn cancel(&mut self) -> Result<(), Self::Error> {
|
|
|
|
if !self.tim.reg().ctrl.read().enable().bit_is_set() {
|
|
|
|
return Err(TimerErrors::Canceled);
|
|
|
|
}
|
|
|
|
self.tim.reg().ctrl.write(|w| w.enable().clear_bit());
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delay for microseconds.
|
|
|
|
///
|
|
|
|
/// For delays less than 100 us, an assembly delay will be used.
|
|
|
|
/// For larger delays, the timer peripheral will be used.
|
|
|
|
/// Please note that the delay using the peripheral might not
|
|
|
|
/// work properly in debug mode.
|
|
|
|
impl<TIM: ValidTim> delay::DelayUs<u32> for CountDownTimer<TIM> {
|
|
|
|
fn delay_us(&mut self, us: u32) {
|
|
|
|
if us < 100 {
|
|
|
|
cortex_m::asm::delay(us * (self.sys_clk.0 / 2_000_000));
|
|
|
|
} else {
|
|
|
|
// Configuring the peripheral for higher frequencies is unstable
|
|
|
|
self.start(1000.khz());
|
|
|
|
// The subtracted value is an empirical value measures by using tests with
|
|
|
|
// an oscilloscope.
|
|
|
|
for _ in 0..us - 7 {
|
|
|
|
nb::block!(self.wait()).unwrap();
|
2021-11-17 10:34:55 +01:00
|
|
|
}
|
2021-12-05 22:57:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Forwards call to u32 variant of delay
|
|
|
|
impl<TIM: ValidTim> delay::DelayUs<u16> for CountDownTimer<TIM> {
|
|
|
|
fn delay_us(&mut self, us: u16) {
|
|
|
|
self.delay_us(u32::from(us));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Forwards call to u32 variant of delay
|
|
|
|
impl<TIM: ValidTim> delay::DelayUs<u8> for CountDownTimer<TIM> {
|
|
|
|
fn delay_us(&mut self, us: u8) {
|
|
|
|
self.delay_us(u32::from(us));
|
|
|
|
}
|
|
|
|
}
|
2021-11-17 10:34:55 +01:00
|
|
|
|
2021-12-05 22:57:54 +01:00
|
|
|
impl<TIM: ValidTim> delay::DelayMs<u32> for CountDownTimer<TIM> {
|
|
|
|
fn delay_ms(&mut self, ms: u32) {
|
|
|
|
self.start(1000.hz());
|
|
|
|
for _ in 0..ms {
|
|
|
|
nb::block!(self.wait()).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<TIM: ValidTim> delay::DelayMs<u16> for CountDownTimer<TIM> {
|
|
|
|
fn delay_ms(&mut self, ms: u16) {
|
|
|
|
self.delay_ms(u32::from(ms));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<TIM: ValidTim> embedded_hal::blocking::delay::DelayMs<u8> for CountDownTimer<TIM> {
|
|
|
|
fn delay_ms(&mut self, ms: u8) {
|
|
|
|
self.delay_ms(u32::from(ms));
|
2021-11-08 01:40:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-13 14:43:21 +01:00
|
|
|
// Set up a millisecond timer on TIM0. Please note that you still need to unmask the related IRQ
|
|
|
|
// and provide an IRQ handler yourself
|
|
|
|
pub fn set_up_ms_timer(
|
|
|
|
syscfg: &mut pac::SYSCONFIG,
|
|
|
|
irqsel: &mut pac::IRQSEL,
|
|
|
|
sys_clk: Hertz,
|
|
|
|
tim0: TIM0,
|
|
|
|
irq: pac::Interrupt,
|
2021-11-17 10:34:55 +01:00
|
|
|
) -> CountDownTimer<TIM0> {
|
2021-12-05 22:57:54 +01:00
|
|
|
let mut ms_timer = CountDownTimer::new(syscfg, sys_clk, tim0);
|
2021-11-13 14:43:21 +01:00
|
|
|
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
|
|
|
|
ms_timer.start(1000.hz());
|
2021-11-17 10:34:55 +01:00
|
|
|
ms_timer
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This function can be called in a specified interrupt handler to increment
|
|
|
|
/// the MS counter
|
|
|
|
pub fn default_ms_irq_handler() {
|
|
|
|
cortex_m::interrupt::free(|cs| {
|
|
|
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
|
|
|
ms += 1;
|
|
|
|
MS_COUNTER.borrow(cs).set(ms);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the current MS tick count
|
|
|
|
pub fn get_ms_ticks() -> u32 {
|
|
|
|
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
2021-11-13 14:43:21 +01:00
|
|
|
}
|
|
|
|
|
2021-11-17 10:34:55 +01:00
|
|
|
//==================================================================================================
|
|
|
|
// Delay implementations
|
|
|
|
//==================================================================================================
|
|
|
|
|
|
|
|
pub struct Delay {
|
|
|
|
cd_tim: CountDownTimer<TIM0>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Delay {
|
|
|
|
pub fn new(tim0: CountDownTimer<TIM0>) -> Self {
|
|
|
|
Delay { cd_tim: tim0 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
|
|
|
|
impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
|
|
|
|
fn delay_ms(&mut self, ms: u32) {
|
|
|
|
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let start_time = get_ms_ticks();
|
|
|
|
while get_ms_ticks() - start_time < ms {
|
|
|
|
cortex_m::asm::nop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|