lpc8xx_hal/
delay.rs

1//! API for delays with the systick timer
2//!
3//! Please be aware of potential overflows when using `delay_us`.
4//! E.g. at 30MHz the maximum delay is 146 seconds.
5//!
6//! # Example
7//!
8//! ``` no_run
9//! use lpc8xx_hal::{
10//!     prelude::*,
11//!     delay::Delay,
12//!     pac::CorePeripherals,
13//! };
14//!
15//! let mut cp = CorePeripherals::take().unwrap();
16//!
17//! let mut delay = Delay::new(cp.SYST);
18//! loop {
19//!     delay.delay_ms(1_000_u16);
20//! }
21//! ```
22
23use cortex_m::peripheral::syst::SystClkSource;
24
25use crate::pac::SYST;
26use embedded_hal::blocking::delay::{DelayMs, DelayUs};
27use embedded_hal_alpha::delay::blocking::DelayUs as DelayUsAlpha;
28use void::Void;
29
30const SYSTICK_RANGE: u32 = 0x0100_0000;
31const SYSTEM_CLOCK: u32 = 12_000_000;
32
33/// System timer (SysTick) as a delay provider
34///
35/// # `embedded-hal` traits
36/// - [`embedded_hal::blocking::delay::DelayUs`]
37/// - [`embedded_hal::blocking::delay::DelayMs`]
38///
39/// [`embedded_hal::blocking::delay::DelayUs`]: #impl-DelayUs%3Cu32%3E
40/// [`embedded_hal::blocking::delay::DelayMs`]: #impl-DelayMs%3Cu32%3E
41#[derive(Clone)]
42pub struct Delay {
43    scale: u32,
44}
45
46impl Delay {
47    /// Configures the system timer (SysTick) as a delay provider
48    pub fn new(mut syst: SYST) -> Self {
49        assert!(SYSTEM_CLOCK >= 1_000_000);
50        let scale = SYSTEM_CLOCK / 1_000_000;
51        syst.set_clock_source(SystClkSource::Core);
52
53        syst.set_reload(SYSTICK_RANGE - 1);
54        syst.clear_current();
55        syst.enable_counter();
56
57        Delay { scale }
58        // As access to the count register is possible without a reference to the systick, we can
59        // safely clone the enabled instance.
60    }
61}
62
63impl DelayMs<u32> for Delay {
64    /// Pauses execution for `ms` milliseconds
65    // At 30 MHz (the maximum frequency), calling delay_us with ms * 1_000 directly overflows at 0x418937 (over the max u16 value)
66    // So we implement a separate, higher level, delay loop
67    fn delay_ms(&mut self, mut ms: u32) {
68        const MAX_MS: u32 = 0x0000_FFFF;
69        while ms != 0 {
70            let current_ms = if ms <= MAX_MS { ms } else { MAX_MS };
71            DelayUs::delay_us(self, current_ms * 1_000);
72            ms -= current_ms;
73        }
74    }
75}
76
77impl DelayMs<u16> for Delay {
78    /// Pauses execution for `ms` milliseconds
79    fn delay_ms(&mut self, ms: u16) {
80        // Call delay_us directly, since we don't have to use the additional
81        // delay loop the u32 variant uses
82        DelayUs::delay_us(self, ms as u32 * 1_000);
83    }
84}
85
86impl DelayMs<u8> for Delay {
87    /// Pauses execution for `ms` milliseconds
88    fn delay_ms(&mut self, ms: u8) {
89        DelayMs::delay_ms(self, ms as u16);
90    }
91}
92
93// At 30MHz (the maximum frequency), this overflows at approx. 2^32 / 30 = 146 seconds
94impl DelayUs<u32> for Delay {
95    /// Pauses execution for `us` microseconds
96    fn delay_us(&mut self, us: u32) {
97        // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
98        // Here half the maximum is used so we have some play if there's a long running interrupt.
99        const MAX_TICKS: u32 = 0x007F_FFFF;
100
101        let mut total_ticks = us * self.scale;
102
103        while total_ticks != 0 {
104            let current_ticks = if total_ticks <= MAX_TICKS {
105                total_ticks
106            } else {
107                MAX_TICKS
108            };
109
110            let start_count = SYST::get_current();
111            total_ticks -= current_ticks;
112
113            // Use the wrapping subtraction and the modulo to deal with the systick wrapping around
114            // from 0 to 0xFFFF
115            while (start_count.wrapping_sub(SYST::get_current())
116                % SYSTICK_RANGE)
117                < current_ticks
118            {}
119        }
120    }
121}
122
123impl DelayUsAlpha for Delay {
124    type Error = Void;
125
126    /// Pauses execution for `us` microseconds
127    fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
128        Ok(DelayUs::delay_us(self, us))
129    }
130}
131
132impl DelayUs<u16> for Delay {
133    /// Pauses execution for `us` microseconds
134    fn delay_us(&mut self, us: u16) {
135        DelayUs::delay_us(self, us as u32)
136    }
137}
138
139impl DelayUs<u8> for Delay {
140    /// Pauses execution for `us` microseconds
141    fn delay_us(&mut self, us: u8) {
142        DelayUs::delay_us(self, us as u32)
143    }
144}