atsamd_hal/
timer_params.rs

1//! helper struct to calculate divider & cycles settings for timers.
2use crate::time::{Hertz, Nanoseconds};
3
4/// Helper type for computing cycles and divider given frequency
5#[derive(Debug, Clone, Copy)]
6pub struct TimerParams {
7    pub divider: u16,
8    pub cycles: u32,
9}
10
11impl TimerParams {
12    /// calculates TimerParams from a given frequency based timeout.
13    pub fn new(timeout: Hertz, src_freq: Hertz) -> Self {
14        let ticks: u32 = src_freq.to_Hz() / timeout.to_Hz().max(1);
15        Self::new_from_ticks(ticks)
16    }
17
18    /// calculates TimerParams from a given period based timeout.
19    pub fn new_ns(timeout: Nanoseconds, src_freq: Hertz) -> Self {
20        let ticks: u32 =
21            (timeout.to_nanos() as u64 * src_freq.to_Hz() as u64 / 1_000_000_000_u64) as u32;
22        Self::new_from_ticks(ticks)
23    }
24
25    fn new_from_ticks(ticks: u32) -> Self {
26        let divider = ((ticks >> 16) + 1).next_power_of_two();
27        let divider = match divider {
28            1 | 2 | 4 | 8 | 16 | 64 | 256 | 1024 => divider,
29            // There are a couple of gaps, so we round up to the next largest
30            // divider; we'll need to count twice as many but it will work.
31            32 => 64,
32            128 => 256,
33            512 => 1024,
34            // Catch all case; this is lame.  Would be great to detect this
35            // and fail at compile time.
36            _ => 1024,
37        };
38
39        let cycles: u32 = ticks / divider;
40
41        if cycles > u16::MAX as u32 {
42            panic!("cycles {} is out of range for a 16 bit counter", cycles);
43        }
44
45        TimerParams {
46            divider: divider as u16,
47            cycles,
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::fugit::{ExtU32, RateExtU32};
55    use crate::timer_params::TimerParams;
56
57    #[test]
58    fn timer_params_hz_and_us_same_1hz() {
59        let tp_from_hz = TimerParams::new(1.Hz(), 48.MHz());
60        let tp_from_us = TimerParams::new_ns(1_000_000.micros(), 48.MHz());
61
62        assert_eq!(tp_from_hz.divider, tp_from_us.divider);
63        assert_eq!(tp_from_hz.cycles, tp_from_us.cycles);
64    }
65
66    #[test]
67    fn timer_params_hz_and_us_same_3hz() {
68        let tp_from_hz = TimerParams::new(3.Hz(), 48.MHz());
69        let tp_from_us = TimerParams::new_ns(333_333.micros(), 48.MHz());
70
71        // There's some rounding error here, but it is extremely small (1 cycle
72        // difference)
73        assert_eq!(tp_from_hz.divider, tp_from_us.divider);
74        assert!((tp_from_hz.cycles as i32 - tp_from_us.cycles as i32).abs() <= 1);
75    }
76}