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;