pub mod prelude {
#[cfg(feature = "stm32_tim2")]
pub use crate::stm32_tim2_monotonic;
#[cfg(feature = "stm32_tim3")]
pub use crate::stm32_tim3_monotonic;
#[cfg(feature = "stm32_tim4")]
pub use crate::stm32_tim4_monotonic;
#[cfg(feature = "stm32_tim5")]
pub use crate::stm32_tim5_monotonic;
#[cfg(feature = "stm32_tim15")]
pub use crate::stm32_tim15_monotonic;
pub use crate::Monotonic;
pub use fugit::{self, ExtU64, ExtU64Ceil};
}
use portable_atomic::{AtomicU64, Ordering};
use rtic_time::{
half_period_counter::calculate_now,
timer_queue::{TimerQueue, TimerQueueBackend},
};
use stm32_metapac as pac;
mod _generated {
#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
}
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_stm32_timer_interrupt {
($mono_backend:ident, $interrupt_name:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn $interrupt_name() {
use $crate::TimerQueueBackend;
$crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt();
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_stm32_timer_struct {
($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
pub struct $name;
impl $name {
pub fn start(tim_clock_hz: u32) {
$crate::__internal_create_stm32_timer_interrupt!($mono_backend, $timer);
$crate::stm32::$mono_backend::_start(tim_clock_hz, $tick_rate_hz);
}
}
impl $crate::TimerQueueBasedMonotonic for $name {
type Backend = $crate::stm32::$mono_backend;
type Instant = $crate::fugit::Instant<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
}
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
};
}
#[cfg(feature = "stm32_tim2")]
#[macro_export]
macro_rules! stm32_tim2_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_struct!($name, Tim2Backend, TIM2, $tick_rate_hz);
};
}
#[cfg(feature = "stm32_tim3")]
#[macro_export]
macro_rules! stm32_tim3_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_struct!($name, Tim3Backend, TIM3, $tick_rate_hz);
};
}
#[cfg(feature = "stm32_tim4")]
#[macro_export]
macro_rules! stm32_tim4_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_struct!($name, Tim4Backend, TIM4, $tick_rate_hz);
};
}
#[cfg(feature = "stm32_tim5")]
#[macro_export]
macro_rules! stm32_tim5_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz);
};
}
#[cfg(feature = "stm32_tim15")]
#[macro_export]
macro_rules! stm32_tim15_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz);
};
}
trait TimerWord: TryFrom<u64> {
const HALF_PERIOD_VALUE: Self;
fn trunc_from_u64(val: u64) -> Self;
}
impl TimerWord for u8 {
const HALF_PERIOD_VALUE: Self = 0x80;
fn trunc_from_u64(val: u64) -> Self {
val as Self
}
}
impl TimerWord for u16 {
const HALF_PERIOD_VALUE: u16 = 0x8000;
fn trunc_from_u64(val: u64) -> Self {
val as Self
}
}
impl TimerWord for u32 {
const HALF_PERIOD_VALUE: u32 = 0x8000_0000;
fn trunc_from_u64(val: u64) -> Self {
val as Self
}
}
impl TimerWord for u64 {
const HALF_PERIOD_VALUE: u64 = 0x8000_0000_0000_0000;
fn trunc_from_u64(val: u64) -> Self {
val as Self
}
}
fn half_period_value<T: TimerWord>(
_: impl FnOnce() -> T,
) -> T {
T::HALF_PERIOD_VALUE
}
fn compute_compare_value<T: TimerWord>(
instant: u64,
now: u64,
_: impl FnOnce() -> T,
) -> T {
let val = if T::try_from(instant.wrapping_sub(now)).is_ok() {
instant
} else {
0
};
T::trunc_from_u64(val)
}
macro_rules! make_timer {
($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
pub struct $backend_name;
use pac::$timer;
static $overflow: AtomicU64 = AtomicU64::new(0);
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $backend_name {
pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
_generated::$timer::enable();
_generated::$timer::reset();
$timer.cr1().modify(|r| r.set_cen(false));
::core::assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
let Ok(psc) = u16::try_from(tim_clock_hz / timer_hz - 1) else {
::core::panic!("Clock prescaler overflowed!");
};
$timer.psc().write(|r| *r = psc);
$timer.dier().modify(|r| r.set_uie(true));
$timer.ccr(0).write(|r| *r = half_period_value(|| $timer.cnt().read()));
$timer.dier().modify(|r| r.set_ccie(0, true));
$timer.egr().write(|r| r.set_ug(true));
$timer.cnt().write(|r| *r = 1);
$timer.sr().write(|r| {
r.0 = !0;
r.set_uif(false);
r.set_ccif(0, false);
r.set_ccif(1, false);
});
$tq.initialize(Self {});
$overflow.store(0, Ordering::SeqCst);
$timer.cr1().modify(|r| {
r.set_cen(true);
});
unsafe {
crate::set_monotonic_prio(_generated::NVIC_PRIO_BITS, pac::Interrupt::$timer);
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer);
}
}
}
impl TimerQueueBackend for $backend_name {
type Ticks = u64;
fn now() -> Self::Ticks {
calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| $timer.cnt().read()
)
}
fn set_compare(instant: Self::Ticks) {
let now = Self::now();
$timer.ccr(1).write(|r| {
*r = compute_compare_value(instant, now, || $timer.cnt().read());
});
}
fn clear_compare_flag() {
$timer.sr().write(|r| {
r.0 = !0;
r.set_ccif(1, false);
});
}
fn pend_interrupt() {
cortex_m::peripheral::NVIC::pend(pac::Interrupt::$timer);
}
fn enable_timer() {
$timer.dier().modify(|r| r.set_ccie(1, true));
}
fn disable_timer() {
$timer.dier().modify(|r| r.set_ccie(1, false));
}
fn on_interrupt() {
if $timer.sr().read().uif() {
$timer.sr().write(|r| {
r.0 = !0;
r.set_uif(false);
});
let prev = $overflow.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 1, "Monotonic must have missed an interrupt!");
}
if $timer.sr().read().ccif(0) {
$timer.sr().write(|r| {
r.0 = !0;
r.set_ccif(0, false);
});
let prev = $overflow.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
}
}
fn timer_queue() -> &'static TimerQueue<$backend_name> {
&$tq
}
}
};
}
macro_rules! make_timer2 {
($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
pub struct $backend_name;
use pac::$timer;
static $overflow: AtomicU64 = AtomicU64::new(0);
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $backend_name {
pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
_generated::$timer::enable();
_generated::$timer::reset();
$timer.cr1().modify(|r| r.set_cen(false));
::core::assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
let Ok(psc) = u16::try_from(tim_clock_hz / timer_hz - 1) else {
::core::panic!("Clock prescaler overflowed!");
};
$timer.psc().write(|r| *r = psc);
$timer.dier().modify(|r| r.set_uie(true));
$timer.ccr(0).write(|r| r.set_ccr(half_period_value(|| $timer.cnt().read().cnt())));
$timer.dier().modify(|r| r.set_ccie(0, true));
$timer.egr().write(|r| r.set_ug(true));
$timer.cnt().write(|r| r.set_cnt(1));
$timer.sr().write(|r| {
r.0 = !0;
r.set_uif(false);
r.set_ccif(0, false);
r.set_ccif(1, false);
});
$tq.initialize(Self {});
$overflow.store(0, Ordering::SeqCst);
$timer.cr1().modify(|r| {
r.set_cen(true);
});
unsafe {
crate::set_monotonic_prio(_generated::NVIC_PRIO_BITS, pac::Interrupt::$timer);
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer);
}
}
}
impl TimerQueueBackend for $backend_name {
type Ticks = u64;
fn now() -> Self::Ticks {
calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| $timer.cnt().read().cnt()
)
}
fn set_compare(instant: Self::Ticks) {
let now = Self::now();
$timer.ccr(1).write(|r| {
r.set_ccr(compute_compare_value(instant, now, || $timer.cnt().read().cnt()));
});
}
fn clear_compare_flag() {
$timer.sr().write(|r| {
r.0 = !0;
r.set_ccif(1, false);
});
}
fn pend_interrupt() {
cortex_m::peripheral::NVIC::pend(pac::Interrupt::$timer);
}
fn enable_timer() {
$timer.dier().modify(|r| r.set_ccie(1, true));
}
fn disable_timer() {
$timer.dier().modify(|r| r.set_ccie(1, false));
}
fn on_interrupt() {
if $timer.sr().read().uif() {
$timer.sr().write(|r| {
r.0 = !0;
r.set_uif(false);
});
let prev = $overflow.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 1, "Monotonic must have missed an interrupt!");
}
if $timer.sr().read().ccif(0) {
$timer.sr().write(|r| {
r.0 = !0;
r.set_ccif(0, false);
});
let prev = $overflow.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
}
}
fn timer_queue() -> &'static TimerQueue<$backend_name> {
&$tq
}
}
};
}
#[cfg(feature = "stm32_tim2")]
make_timer!(Tim2Backend, TIM2, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(feature = "stm32_tim3")]
make_timer2!(Tim3Backend, TIM3, TIMER3_OVERFLOWS, TIMER3_TQ);
#[cfg(feature = "stm32_tim4")]
make_timer2!(Tim4Backend, TIM4, TIMER4_OVERFLOWS, TIMER4_TQ);
#[cfg(feature = "stm32_tim5")]
make_timer!(Tim5Backend, TIM5, TIMER5_OVERFLOWS, TIMER5_TQ);
#[cfg(feature = "stm32_tim15")]
make_timer2!(Tim15Backend, TIM15, TIMER15_OVERFLOWS, TIMER15_TQ);