embedded_async_timer/impls/
stm32f103_rtc.rs

1//! A simple AsyncTimer implementation for the Rtc peripheral in the STM32F103 running at 1kHz.
2
3use bare_metal::CriticalSection;
4use core::sync::atomic::{self, Ordering};
5use stm32f1xx_hal::{
6    pac,
7    prelude::*,
8    rtc::Rtc,
9    stm32::{Interrupt, NVIC},
10};
11
12use crate::{AsyncTimer, Timer};
13
14const TICKS_PER_SECOND: u64 = 1000;
15
16/// An instant of time, encoding the RTC instant value exactly as the RTC peripheral does.
17#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Copy, Debug)]
18pub struct InstantU32(u32);
19
20/// A segment of time, encoding a duration in such a manner that it compares and adds to a RTC instant efficiently.
21#[derive(Clone)]
22pub struct DurationU32(u32);
23
24impl core::convert::From<core::time::Duration> for DurationU32 {
25    fn from(duration: core::time::Duration) -> Self {
26        Self(
27            ((duration.as_secs() as u64) * TICKS_PER_SECOND
28                + (duration.subsec_nanos() as u64) * TICKS_PER_SECOND / 1_000_000_000)
29                as u32,
30        )
31    }
32}
33
34impl core::ops::AddAssign<DurationU32> for InstantU32 {
35    fn add_assign(&mut self, duration: DurationU32) {
36        self.0 += duration.0;
37    }
38}
39
40impl core::ops::Add<DurationU32> for InstantU32 {
41    type Output = Self;
42
43    fn add(mut self, rhs: DurationU32) -> Self::Output {
44        self += rhs;
45        self
46    }
47}
48
49impl Timer for Rtc {
50    type Instant = InstantU32;
51    type Duration = DurationU32;
52
53    const DELTA: DurationU32 = DurationU32(2);
54
55    fn reset(&mut self) {
56        unsafe { NVIC::unmask(Interrupt::RTC) }
57        self.select_frequency((TICKS_PER_SECOND as u32).hz());
58        self.set_time(0);
59    }
60
61    #[inline(always)]
62    fn interrupt_free<F: FnOnce(&CriticalSection) -> R, R>(f: F) -> R {
63        cortex_m::interrupt::free(f)
64    }
65
66    #[inline(always)]
67    fn now(&self) -> Self::Instant {
68        InstantU32(self.current_time())
69    }
70
71    #[inline(always)]
72    fn disarm(&mut self) {
73        self.unlisten_alarm();
74        self.clear_alarm_flag();
75    }
76
77    #[inline(always)]
78    fn arm(&mut self, deadline: &Self::Instant) {
79        // Assumes the alarm was already disarmed.
80        self.set_alarm(deadline.0);
81        self.clear_alarm_flag();
82        self.listen_alarm();
83    }
84}
85
86/// Handle the RTC interrupt alarm and wake up the appropriate waker.
87///
88/// Add to your code:
89/// ```
90/// static mut TIMER: Option<AsyncTimer<Rtc>> = None;
91///
92/// #[interrupt]
93/// #[allow(non_snake_case)]
94/// #[no_mangle]
95/// fn RTC() {
96///     if let Some(timer) = unsafe { TIMER.as_ref() } {
97///         handle_interrupt(move || timer)
98///     }
99/// }
100/// ```
101#[inline(always)]
102pub fn handle_interrupt<'a>(get_timer: impl FnOnce() -> &'a AsyncTimer<Rtc>) {
103    if swap_check() {
104        get_timer().awaken();
105    }
106}
107
108#[inline(always)]
109fn swap_check() -> bool {
110    stm32f1xx_hal::pac::RTC::borrow_unchecked(|rtc| {
111        if rtc.crl.read().alrf().bit() {
112            rtc.crl.modify(|_, w| w.alrf().clear_bit());
113            true
114        } else {
115            false
116        }
117    })
118}
119
120macro_rules! borrow_unchecked {
121    ($($peripheral:ident),*) => {
122        $(
123            unsafe impl BorrowUnchecked for pac::$peripheral {
124                #[inline(always)]
125                fn borrow_unchecked<T>(f: impl FnOnce(&Self) -> T) -> T {
126                    let p = unsafe { core::mem::transmute(()) };
127                    f(&p)
128                }
129            }
130        )*
131    }
132}
133
134/// Borrows a peripheral without checking if it has already been taken
135unsafe trait BorrowUnchecked {
136    fn borrow_unchecked<T>(f: impl FnOnce(&Self) -> T) -> T;
137}
138
139borrow_unchecked!(RTC);