Last active
October 27, 2020 16:37
-
-
Save andresv/7966cdda67753813705c79737e1c18cc to your computer and use it in GitHub Desktop.
stm32g0 rtfm::Monotonic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Using STM32G0 TIM2 as monotonic timer | |
use core::u32; | |
use core::{ | |
cmp::Ordering, | |
convert::{Infallible, TryInto}, | |
ops, | |
}; | |
use defmt::Format; | |
use hal::stm32; | |
use hal::timer::*; | |
use rtic::{Fraction, Monotonic}; | |
/// A measurement of the counter. Opaque and useful only with `Duration` | |
/// | |
/// # Correctness | |
/// | |
/// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively | |
/// makes it "wrap around" and creates an incorrect value. This is also true if the operation is | |
/// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks. | |
#[derive(Format, Clone, Copy, Eq, PartialEq)] | |
pub struct Instant { | |
inner: i32, | |
} | |
impl Instant { | |
/// Returns an instant corresponding to "now" | |
pub fn now() -> Self { | |
unsafe { | |
let tim = &*stm32::TIM2::ptr(); | |
let cnt = tim.cnt.read().bits() as i32; | |
Instant { | |
inner: cnt, | |
} | |
} | |
} | |
/// Returns the amount of time elapsed since this instant was created. | |
pub fn elapsed(&self) -> Duration { | |
Instant::now() - *self | |
} | |
/// Returns the underlying count | |
pub fn counts(&self) -> u32 { | |
self.inner as u32 | |
} | |
/// Returns the amount of time elapsed from another instant to this one. | |
/// NOTE: it does not handle timer wraping correctly! | |
pub fn duration_since(&self, earlier: Instant) -> Duration { | |
let diff = self.inner - earlier.inner; | |
assert!(diff >= 0, "second instant is later than self"); | |
Duration { inner: diff as u32 } | |
} | |
} | |
impl ops::AddAssign<Duration> for Instant { | |
fn add_assign(&mut self, dur: Duration) { | |
// NOTE this is a debug assertion because there's no foolproof way to detect a wrap around; | |
// the user may write `(instant + dur) + dur` where `dur` is `(1<<31)-1` ticks. | |
debug_assert!(dur.inner < (1 << 31)); | |
self.inner = self.inner.wrapping_add(dur.inner as i32); | |
} | |
} | |
impl ops::Add<Duration> for Instant { | |
type Output = Self; | |
fn add(mut self, dur: Duration) -> Self { | |
self += dur; | |
self | |
} | |
} | |
impl ops::SubAssign<Duration> for Instant { | |
fn sub_assign(&mut self, dur: Duration) { | |
// NOTE see the NOTE in `<Instant as AddAssign<Duration>>::add_assign` | |
debug_assert!(dur.inner < (1 << 31)); | |
self.inner = self.inner.wrapping_sub(dur.inner as i32); | |
} | |
} | |
impl ops::Sub<Duration> for Instant { | |
type Output = Self; | |
fn sub(mut self, dur: Duration) -> Self { | |
self -= dur; | |
self | |
} | |
} | |
impl ops::Sub<Instant> for Instant { | |
type Output = Duration; | |
fn sub(self, other: Instant) -> Duration { | |
self.duration_since(other) | |
} | |
} | |
impl Ord for Instant { | |
fn cmp(&self, rhs: &Self) -> Ordering { | |
self.inner.wrapping_sub(rhs.inner).cmp(&0) | |
} | |
} | |
impl PartialOrd for Instant { | |
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { | |
Some(self.cmp(rhs)) | |
} | |
} | |
/// A `Duration` type to represent a span of time. | |
/// | |
/// # Correctness | |
/// | |
/// This type is *not* appropriate for representing time spans in the order of, or larger than, | |
/// seconds because it can hold a maximum of `(1 << 31)` "ticks" where each tick is the inverse of | |
/// the CPU frequency, which usually is dozens of MHz. | |
#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] | |
pub struct Duration { | |
inner: u32, | |
} | |
impl Duration { | |
/// Creates a new `Duration` from the specified number of clock cycles | |
pub fn from_cycles(cycles: u32) -> Self { | |
Duration { inner: cycles } | |
} | |
/// Returns the total number of clock cycles contained by this `Duration` | |
pub fn as_cycles(&self) -> u32 { | |
self.inner | |
} | |
/// Returns `Duration` in microseconds | |
pub fn micros(&self) -> u32 { | |
let frac = Tim2::ratio(); | |
self.inner * frac.numerator / (64 * frac.denominator) | |
} | |
} | |
// Used internally by RTFM to convert the duration into a known type | |
impl TryInto<u32> for Duration { | |
type Error = Infallible; | |
fn try_into(self) -> Result<u32, Infallible> { | |
Ok(self.as_cycles()) | |
} | |
} | |
impl ops::AddAssign for Duration { | |
fn add_assign(&mut self, dur: Duration) { | |
self.inner += dur.inner; | |
} | |
} | |
impl ops::Add<Duration> for Duration { | |
type Output = Self; | |
fn add(self, other: Self) -> Self { | |
Duration { | |
inner: self.inner + other.inner, | |
} | |
} | |
} | |
impl ops::Mul<u32> for Duration { | |
type Output = Self; | |
fn mul(self, other: u32) -> Self { | |
Duration { | |
inner: self.inner * other, | |
} | |
} | |
} | |
impl ops::MulAssign<u32> for Duration { | |
fn mul_assign(&mut self, other: u32) { | |
*self = *self * other; | |
} | |
} | |
impl ops::SubAssign for Duration { | |
fn sub_assign(&mut self, rhs: Duration) { | |
self.inner -= rhs.inner; | |
} | |
} | |
impl ops::Sub<Duration> for Duration { | |
type Output = Self; | |
fn sub(self, rhs: Self) -> Self { | |
Duration { | |
inner: self.inner - rhs.inner, | |
} | |
} | |
} | |
/// Adds the `secs`, `millis` and `micros` methods to the `u32` type | |
pub trait U32Ext { | |
/// Converts the `u32` value as seconds into ticks | |
fn secs(self) -> Duration; | |
/// Converts the `u32` value as milliseconds into ticks | |
fn millis(self) -> Duration; | |
/// Converts the `u32` value as microseconds into ticks | |
fn micros(self) -> Duration; | |
} | |
impl U32Ext for u32 { | |
fn secs(self) -> Duration { | |
self.millis() * 1_000 | |
} | |
fn millis(self) -> Duration { | |
self.micros() * 1_000 | |
} | |
fn micros(self) -> Duration { | |
let frac = Tim2::ratio(); | |
// 64 MHz / fraction / 1_000_000 | |
Duration { | |
inner: (64 * frac.denominator * self) / frac.numerator, | |
} | |
} | |
} | |
/// Implementation of the `Monotonic` trait | |
pub struct Tim2; | |
impl Tim2 { | |
// consume timer so it cannot be used accidentially elsewhere | |
pub fn init(_: Timer<stm32::TIM2>) { | |
let tim2 = unsafe { &*stm32::TIM2::ptr() }; | |
tim2.arr.write(|w| unsafe { w.bits(u32::MAX) }); | |
// 64 MHz prescaled by 64 (psc is -1) = 1 MHz | |
tim2.psc.write(|w| unsafe { w.bits(63) }); | |
tim2.cnt.write(|w| unsafe { w.bits(0) }); | |
tim2.egr.write(|w| w.ug().set_bit()); | |
tim2.cr1.modify(|_, w| w.cen().set_bit().urs().set_bit()); | |
} | |
} | |
impl Monotonic for Tim2 { | |
type Instant = Instant; | |
fn ratio() -> Fraction { | |
// monotonic * fraction = sys clock | |
Fraction { | |
numerator: 64, | |
denominator: 1, | |
} | |
} | |
unsafe fn reset() { | |
let ptr = &*stm32::TIM2::ptr(); | |
ptr.cnt.reset(); | |
} | |
fn now() -> Instant { | |
Instant::now() | |
} | |
fn zero() -> Instant { | |
Instant { | |
inner: 0, | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment