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}