avr_hal_generic/
delay.rs

1//! Delay implementations
2
3use core::marker;
4use embedded_hal::delay::DelayNs;
5use embedded_hal_v0::blocking::delay as delay_v0;
6
7#[cfg(target_arch = "avr")]
8use core::arch::asm;
9
10/// A busy-loop delay implementation
11///
12/// # Example
13/// ```rust
14/// // Instead of arduino_hal below you may also use a different
15/// // HAL based on avr_hal_generic like attiny_hal or atmega_hal
16/// // depending on actual hardware. For example:
17/// //
18/// // use attiny_hal as hal;
19///
20/// use arduino_hal as hal;
21/// use embedded_hal_v0::prelude::*;
22///
23/// let mut delay = embedded_hal_v0::delay::Delay::<hal::clock::MHz16>::new();
24///
25/// // Wait 1 second
26/// delay.delay_ms(1000);
27/// ```
28///
29/// # Warning
30/// The delay is not accurate for values above 4095µs because of a loop whose
31/// overhead is not accounted for.  This will be fixed in a future version.
32#[derive(Debug, Clone, Copy)]
33pub struct Delay<SPEED> {
34    _speed: marker::PhantomData<SPEED>,
35}
36
37impl<SPEED> Delay<SPEED> {
38    pub fn new() -> Delay<SPEED> {
39        Delay {
40            _speed: marker::PhantomData,
41        }
42    }
43}
44
45// based on https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c
46
47#[cfg(target_arch = "avr")]
48#[allow(unused_assignments)]
49fn busy_loop(mut c: u16) {
50    unsafe {
51        asm!(
52            "1:",
53            "sbiw {c}, 1",
54            "brne 1b",
55            c = inout(reg_iw) c,
56        );
57    }
58}
59
60#[cfg(not(target_arch = "avr"))]
61fn busy_loop(_c: u16) {
62    unimplemented!("Implementation is only available for avr targets!")
63}
64
65// Clock-Specific Delay Implementations ----------------------------------- {{{
66impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz24> {
67    fn delay_us(&mut self, mut us: u16) {
68        // for the 24 crate::clock::MHz clock for the aventurous ones, trying to overclock
69
70        // zero delay fix
71        if us == 0 {
72            return;
73        } // = 3 cycles, (4 when true)
74
75        // the following loop takes a 1/6 of a microsecond (4 cycles)
76        // per iteration, so execute it six times for each microsecond of
77        // delay requested.
78        us *= 6; // x6 us, = 7 cycles
79
80        // account for the time taken in the preceeding commands.
81        // we just burned 22 (24) cycles above, remove 5, (5*4=20)
82        // us is at least 6 so we can substract 5
83        us -= 5; //=2 cycles
84
85        busy_loop(us);
86    }
87}
88
89impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz20> {
90    fn delay_us(&mut self, mut us: u16) {
91        // for the 20 crate::clock::MHz clock on rare Arduino boards
92
93        // for a one-microsecond delay, simply return.  the overhead
94        // of the function call takes 18 (20) cycles, which is 1us
95        #[cfg(target_arch = "avr")]
96        unsafe {
97            asm!("nop", "nop", "nop", "nop");
98        }
99
100        if us <= 1 {
101            return;
102        } // = 3 cycles, (4 when true)
103
104        // the following loop takes a 1/5 of a microsecond (4 cycles)
105        // per iteration, so execute it five times for each microsecond of
106        // delay requested.
107        us = (us << 2) + us; // x5 us, = 7 cycles
108
109        // account for the time taken in the preceeding commands.
110        // we just burned 26 (28) cycles above, remove 7, (7*4=28)
111        // us is at least 10 so we can substract 7
112        us -= 7; // 2 cycles
113
114        busy_loop(us);
115    }
116}
117
118impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz16> {
119    fn delay_us(&mut self, mut us: u16) {
120        // for the 16 crate::clock::MHz clock on most Arduino boards
121
122        // for a one-microsecond delay, simply return.  the overhead
123        // of the function call takes 14 (16) cycles, which is 1us
124        if us <= 1 {
125            return;
126        } // = 3 cycles, (4 when true)
127
128        // the following loop takes 1/4 of a microsecond (4 cycles)
129        // per iteration, so execute it four times for each microsecond of
130        // delay requested.
131        us <<= 2; // x4 us, = 4 cycles
132
133        // account for the time taken in the preceeding commands.
134        // we just burned 19 (21) cycles above, remove 5, (5*4=20)
135        // us is at least 8 so we can substract 5
136        us -= 5; // = 2 cycles,
137
138        busy_loop(us);
139    }
140}
141
142impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz12> {
143    fn delay_us(&mut self, mut us: u16) {
144        // for the 12 crate::clock::MHz clock if somebody is working with USB
145
146        // for a 1 microsecond delay, simply return.  the overhead
147        // of the function call takes 14 (16) cycles, which is 1.5us
148        if us <= 1 {
149            return;
150        } // = 3 cycles, (4 when true)
151
152        // the following loop takes 1/3 of a microsecond (4 cycles)
153        // per iteration, so execute it three times for each microsecond of
154        // delay requested.
155        us = (us << 1) + us; // x3 us, = 5 cycles
156
157        // account for the time taken in the preceeding commands.
158        // we just burned 20 (22) cycles above, remove 5, (5*4=20)
159        // us is at least 6 so we can substract 5
160        us -= 5; //2 cycles
161
162        busy_loop(us);
163    }
164}
165
166impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz10> {
167    fn delay_us(&mut self, mut us: u16) {
168        // for the 10 crate::clock::MHz clock if somebody is working with USB
169
170        // for a 1 microsecond delay, simply return.  the overhead
171        // of the function call takes 14 (16) cycles, which is 1.5us
172        if us <= 1 {
173            return;
174        } // = 3 cycles, (4 when true)
175
176        // 4 cycles per busy_loop iteration = 0.4 us per busy loop, so 2.5 times to get 1 us
177        us = ((us << 2) + us) >> 1; // x2.5
178
179        busy_loop(us);
180    }
181}
182
183impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz8> {
184    fn delay_us(&mut self, mut us: u16) {
185        // for the 8 crate::clock::MHz internal clock
186
187        // for a 1 and 2 microsecond delay, simply return.  the overhead
188        // of the function call takes 14 (16) cycles, which is 2us
189        if us <= 2 {
190            return;
191        } // = 3 cycles, (4 when true)
192
193        // the following loop takes 1/2 of a microsecond (4 cycles)
194        // per iteration, so execute it twice for each microsecond of
195        // delay requested.
196        us <<= 1; //x2 us, = 2 cycles
197
198        // account for the time taken in the preceeding commands.
199        // we just burned 17 (19) cycles above, remove 4, (4*4=16)
200        // us is at least 6 so we can substract 4
201        us -= 4; // = 2 cycles
202
203        busy_loop(us);
204    }
205}
206
207impl delay_v0::DelayUs<u16> for Delay<crate::clock::MHz1> {
208    fn delay_us(&mut self, mut us: u16) {
209        // for the 1 crate::clock::MHz internal clock (default settings for common Atmega microcontrollers)
210
211        // the overhead of the function calls is 14 (16) cycles
212        if us <= 16 {
213            return;
214        } //= 3 cycles, (4 when true)
215        if us <= 25 {
216            return;
217        } //= 3 cycles, (4 when true), (must be at least 25 if we want to substract 22)
218
219        // compensate for the time taken by the preceeding and next commands (about 22 cycles)
220        us -= 22; // = 2 cycles
221                  // the following loop takes 4 microseconds (4 cycles)
222                  // per iteration, so execute it us/4 times
223                  // us is at least 4, divided by 4 gives us 1 (no zero delay bug)
224        us >>= 2; // us div 4, = 4 cycles
225
226        busy_loop(us);
227    }
228}
229
230// ------------------------------------------------------------------------ }}}
231
232impl<SPEED> delay_v0::DelayUs<u8> for Delay<SPEED>
233where
234    Delay<SPEED>: delay_v0::DelayUs<u16>,
235{
236    fn delay_us(&mut self, us: u8) {
237        delay_v0::DelayUs::<u16>::delay_us(self, us as u16);
238    }
239}
240
241impl<SPEED> delay_v0::DelayUs<u32> for Delay<SPEED>
242where
243    Delay<SPEED>: delay_v0::DelayUs<u16>,
244{
245    fn delay_us(&mut self, us: u32) {
246        // TODO: Somehow fix the overhead induced by this loop
247        // This was previously a range-based for loop, but that would
248        // compile down to fairly poor code. This is slightly better,
249        // but still has some overhead and may not lead to cycle-accurate
250        // delays.
251        let iters = us >> 12;
252        let mut i = 0;
253        while i < iters {
254            delay_v0::DelayUs::<u16>::delay_us(self, 0xfff);
255            i += 1;
256        }
257        delay_v0::DelayUs::<u16>::delay_us(self, (us & 0xfff) as u16);
258    }
259}
260
261impl<SPEED> delay_v0::DelayMs<u16> for Delay<SPEED>
262where
263    Delay<SPEED>: delay_v0::DelayUs<u32>,
264{
265    fn delay_ms(&mut self, ms: u16) {
266        delay_v0::DelayUs::<u32>::delay_us(self, ms as u32 * 1000);
267    }
268}
269
270impl<SPEED> delay_v0::DelayMs<u8> for Delay<SPEED>
271where
272    Delay<SPEED>: delay_v0::DelayMs<u16>,
273{
274    fn delay_ms(&mut self, ms: u8) {
275        delay_v0::DelayMs::<u16>::delay_ms(self, ms as u16);
276    }
277}
278
279impl<SPEED> DelayNs for Delay<SPEED>
280where
281    Delay<SPEED>: delay_v0::DelayUs<u16>,
282{
283    fn delay_ns(&mut self, ns: u32) {
284        // quick-win to get an initial implementation.
285        // note that the trait does not guarantee nanosecond-accuracy.
286        delay_v0::DelayUs::<u32>::delay_us(self, ns.div_ceil(1000))
287    }
288
289    fn delay_us(&mut self, us: u32) {
290        delay_v0::DelayUs::<u32>::delay_us(self, us);
291    }
292}