1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! # Delay driver
//!
//! ## Overview
//! The Delay driver provides blocking delay functionalities using the
//! `SYSTIMER` peripheral for RISC-V devices and the built-in Xtensa timer for
//! Xtensa devices. This module implements the blocking [DelayMs] and [DelayUs]
//! traits from [embedded-hal].
//!
//! The delays are implemented in a "best-effort" way, meaning that the CPU will
//! block for at least the amount of time specified, but accuracy can be
//! affected by many factors, including interrupt usage.
//!
//! ## Example
//! ```no_run
//! let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze();
//! let mut delay = Delay::new(&clocks);
//!
//! delay.delay_ms(1000 as u32);
//! ```
//!
//! [DelayMs]: embedded_hal::blocking::delay::DelayMs
//! [DelayUs]: embedded_hal::blocking::delay::DelayUs
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/

use fugit::HertzU64;

/// Delay driver
///
/// Uses the `SYSTIMER` peripheral internally for RISC-V devices, and the
/// built-in Xtensa timer for Xtensa devices.
#[derive(Clone, Copy)]
pub struct Delay {
    freq: HertzU64,
}

impl<T> embedded_hal::blocking::delay::DelayMs<T> for Delay
where
    T: Into<u32>,
{
    fn delay_ms(&mut self, ms: T) {
        for _ in 0..ms.into() {
            self.delay_micros(1000u32);
        }
    }
}

impl<T> embedded_hal::blocking::delay::DelayUs<T> for Delay
where
    T: Into<u32>,
{
    fn delay_us(&mut self, us: T) {
        self.delay_micros(us.into());
    }
}

#[cfg(feature = "eh1")]
impl embedded_hal_1::delay::DelayNs for Delay {
    fn delay_ns(&mut self, ns: u32) {
        self.delay_nanos(ns);
    }
}

#[cfg(riscv)]
mod implementation {
    use super::*;
    use crate::{clock::Clocks, systimer::SystemTimer};

    impl Delay {
        /// Create a new `Delay` instance
        pub fn new(clocks: &Clocks) -> Self {
            // The counters and comparators are driven using `XTAL_CLK`.
            // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
            // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
            Self {
                freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 25),
            }
        }

        /// Delay for the specified number of microseconds
        pub fn delay_micros(&self, us: u32) {
            let t0 = SystemTimer::now();
            let clocks = us as u64 * (self.freq / HertzU64::MHz(1));

            while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
        }

        /// Delay for the specified number of nanoseconds
        pub fn delay_nanos(&self, ns: u32) {
            let t0 = SystemTimer::now();
            let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000;

            while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
        }
    }
}

#[cfg(xtensa)]
mod implementation {
    use super::*;
    use crate::clock::Clocks;

    impl Delay {
        /// Create a new `Delay` instance
        pub fn new(clocks: &Clocks) -> Self {
            Self {
                freq: clocks.cpu_clock.into(),
            }
        }

        /// Delay for the specified number of microseconds
        pub fn delay_micros(&self, us: u32) {
            let clocks = us as u64 * (self.freq / HertzU64::MHz(1));
            xtensa_lx::timer::delay(clocks as u32);
        }

        /// Delay for the specified number of nanoseconds
        pub fn delay_nanos(&self, ns: u32) {
            let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000;
            xtensa_lx::timer::delay(clocks as u32);
        }
    }
}