lpc8xx_hal/mrt/
ticks.rs

1use core::{convert::TryFrom, ops};
2
3use embedded_time::duration::{
4    Microseconds, Milliseconds, Nanoseconds, Seconds,
5};
6
7use super::MAX_VALUE;
8
9/// Represents a number of ticks of the MRT timer
10///
11/// `Ticks` has various `From` and `TryFrom` implementations that provide
12/// integration with `embedded_time` duration types. This not only provides a
13/// more convenient API, it also makes it possible to use the MRT generically,
14/// through the [`CountDown`] trait and a bound like
15/// `Timer::Time: TryFrom<Milliseconds>`, without requiring any knowledge of the
16/// timer's frequency.
17///
18/// However, these conversions have performance implications. For best results,
19/// you should use constants for the original values that you want to convert,
20/// to give the compiler a chance to perform the conversion at compile-time.
21///
22/// # Math
23///
24/// `Ticks` supports addition and subtraction via [`core::ops::Add`] and
25/// [`core::ops::Sub`]. Those operations are saturating at the numeric bounds
26/// (0 and [`MAX_VALUE`]) instead of overflowing.
27///
28/// [`CountDown`]: embedded_hal::timer::CountDown
29#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
30pub struct Ticks(pub(super) u32);
31
32impl Ticks {
33    /// Creates a `Tick` instance with the given number of ticks
34    ///
35    /// This method is provided as a fallback to avoid performance overhead, in
36    /// case the user knows that `value` fits within `MAX_VALUE`, but the
37    /// compiler can't perform the necessary optimization. Please use any of the
38    /// `From` or `TryFrom` implementations instead, if you can afford it.
39    ///
40    /// # Safety
41    ///
42    /// The user must guarantee that `value <= MAX_VALUE`.
43    pub unsafe fn from_u32(value: u32) -> Self {
44        Self(value)
45    }
46
47    /// Returns the number of ticks of this `Tick` instance.
48    /// You may also use the `Into` implementations instead.
49    pub fn to_u32(&self) -> u32 {
50        self.0
51    }
52}
53
54impl num_traits::Zero for Ticks {
55    fn zero() -> Self {
56        Self(0)
57    }
58
59    fn is_zero(&self) -> bool {
60        self.0.is_zero()
61    }
62}
63
64impl ops::Add for Ticks {
65    type Output = Self;
66
67    fn add(self, rhs: Self) -> Self::Output {
68        let sum = self.0.saturating_add(rhs.0);
69        let ticks = sum.clamp(sum, MAX_VALUE.0);
70        Self(ticks)
71    }
72}
73
74impl ops::AddAssign for Ticks {
75    fn add_assign(&mut self, rhs: Self) {
76        *self = *self + rhs;
77    }
78}
79
80impl ops::Sub for Ticks {
81    type Output = Self;
82
83    fn sub(self, rhs: Self) -> Self::Output {
84        let difference = self.0.saturating_sub(rhs.0);
85        Self(difference)
86    }
87}
88
89impl ops::SubAssign for Ticks {
90    fn sub_assign(&mut self, rhs: Self) {
91        *self = *self - rhs;
92    }
93}
94
95impl TryFrom<u32> for Ticks {
96    type Error = TickConversionError;
97
98    fn try_from(value: u32) -> Result<Self, Self::Error> {
99        if value > MAX_VALUE.0 {
100            return Err(TickConversionError);
101        }
102
103        Ok(Self(value))
104    }
105}
106
107impl From<Ticks> for u32 {
108    fn from(ticks: Ticks) -> Self {
109        ticks.0
110    }
111}
112
113// Eventually, `Ticks` will need a const-generic argument or something, but as
114// long as everything is hardcoded to 12 MHz, the following will do.
115
116impl From<Nanoseconds> for Ticks {
117    fn from(value: Nanoseconds) -> Self {
118        // This can't possibly fail:
119        // - The multiplication can't overflow after converting to `u64`.
120        // - After the division, the value is guaranteed to fit into the `u32`
121        //   again.
122        // - The maximum possible `value` leads to a result that is smaller than
123        //   `MAX_VALUE`.
124        Self((value.0 as u64 * 12 / 1_000) as u32)
125    }
126}
127
128impl TryFrom<Microseconds> for Ticks {
129    type Error = TickConversionError;
130
131    fn try_from(value: Microseconds) -> Result<Self, Self::Error> {
132        let value = value.0.checked_mul(12).ok_or(TickConversionError)?;
133        Self::try_from(value)
134    }
135}
136
137impl TryFrom<Milliseconds> for Ticks {
138    type Error = TickConversionError;
139
140    fn try_from(value: Milliseconds) -> Result<Self, Self::Error> {
141        let value = value.0.checked_mul(12_000).ok_or(TickConversionError)?;
142        Self::try_from(value)
143    }
144}
145
146impl TryFrom<Seconds> for Ticks {
147    type Error = TickConversionError;
148
149    fn try_from(value: Seconds) -> Result<Self, Self::Error> {
150        let value =
151            value.0.checked_mul(12_000_000).ok_or(TickConversionError)?;
152        Self::try_from(value)
153    }
154}
155
156#[derive(Debug, Eq, PartialEq)]
157/// Indicates that a conversion to [`Ticks`] failed
158///
159/// This is the case when the resulting value is larger than [`MAX_VALUE`].
160pub struct TickConversionError;