Skip to content

Commit 124751f

Browse files
lord-nestappersg
authored andcommitted
Implement delay, delay_ms, and delay_us
Refactor delay_ms and delay_us to prevent overflow . Change delay_ms and delay_us to call the internal delay function with a 48 bit counter. This prevents delay_ms and delay_us from overflowing when passed a large argument. . Move the internal delay functions to their own module. Remove the unused internal delay_loop_3_cycles function.
1 parent 258f391 commit 124751f

File tree

2 files changed

+64
-89
lines changed

2 files changed

+64
-89
lines changed

src/delay_impl.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#[allow(unused_imports)]
2+
use core::arch::asm;
3+
4+
/// Internal function to implement a variable busy-wait loop.
5+
/// # Arguments
6+
/// * 'count' - an u32, the number of times to cycle the loop (4 clock cycles per loop).
7+
#[inline(always)]
8+
pub fn delay_count_32(count: u32) {
9+
let mut outer_count: u16 = (count >> 16) as u16;
10+
let inner_count: u16 = count as u16;
11+
if inner_count != 0 {
12+
delay_loop_4_cycles(inner_count);
13+
}
14+
while outer_count != 0 {
15+
delay_loop_4_cycles(0);
16+
outer_count -= 1;
17+
}
18+
}
19+
20+
/// Internal function to implement a variable busy-wait loop.
21+
/// # Arguments
22+
/// * 'count' - an u64, the number of times to cycle the loop (4 clock cycles per loop). *The top 16 bits are ignored.*
23+
#[inline(always)]
24+
pub fn delay_count_48(count: u64) {
25+
let mut outer_count: u32 = (count >> 16) as u32;
26+
let inner_count: u16 = count as u16;
27+
if inner_count != 0 {
28+
delay_loop_4_cycles(inner_count);
29+
}
30+
while outer_count != 0 {
31+
delay_loop_4_cycles(0);
32+
outer_count -= 1;
33+
}
34+
}
35+
36+
/// Internal function to implement a 16-bit busy-wait loop in assembly.
37+
/// Delays for 4 cycles per iteration, not including setup overhead.
38+
/// Up to 2^16 iterations (the value 2^16 would have to be passed as 0).
39+
/// # Arguments
40+
/// * 'cycles' - an u16, the number of times to cycle the loop.
41+
#[inline(always)]
42+
#[allow(unused_variables, unused_mut, unused_assignments, dead_code)]
43+
pub fn delay_loop_4_cycles(mut cycles: u16) {
44+
#[cfg(target_arch = "avr")]
45+
unsafe {
46+
asm!("1: sbiw {i}, 1",
47+
"brne 1b",
48+
i = inout(reg_iw) cycles => _,
49+
)
50+
}
51+
// Allow compilation even on non-avr targets, for testing purposes
52+
}

src/lib.rs

+12-89
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
#![no_std]
33
#![crate_name = "avr_delay"]
44

5-
#[allow(unused_imports)]
6-
use core::arch::asm;
5+
mod delay_impl;
76

87
/// This library is intended to provide a busy-wait delay
98
/// similar to the one provided by the arduino c++ utilities
@@ -13,110 +12,34 @@ use core::arch::asm;
1312
// This library does all of the busy-wait loop in rust.
1413
// We pack as much of the looping as possible into asm!
1514
// so that we can count cycles.
16-
//
17-
// Ignoring the overhead, which may be significant:
18-
// An arduino runs at 16MHZ. Each asm loop is 4 cycles.
19-
// so each loop is 0.25 us.
20-
//
21-
// the overhead of delay() seems to be about 13 cycles
22-
// initially, and then 11 cycles per outer loop. We ignore
23-
// all that.
2415

25-
/// Internal function to implement a variable busy-wait loop. It is not recommended to use this function directly.
16+
/// Internal function to implement a variable busy-wait loop. Even if count isn't
17+
/// known at compile time, this function shouldn't have too much overhead.
2618
/// # Arguments
2719
/// * 'count' - an u32, the number of times to cycle the loop.
2820
#[inline(always)]
2921
pub fn delay(count: u32) {
30-
delay_count_32(count);
22+
delay_impl::delay_count_32(count);
3123
}
3224

3325
///delay for N milliseconds
3426
/// # Arguments
35-
/// * 'ms' - an u32, number of milliseconds to busy-wait
27+
/// * 'ms' - an u32, number of milliseconds to busy-wait. This should be known at
28+
/// compile time, otherwise the delay may be much longer than specified.
3629
#[inline(always)]
3730
pub fn delay_ms(ms: u32) {
38-
// microseconds
39-
let us = ms * 1000;
40-
delay_us(us);
31+
let ticks: u64 = (u64::from(avr_config::CPU_FREQUENCY_HZ) * u64::from(ms)) / 4_000;
32+
delay_impl::delay_count_48(ticks);
4133
}
4234

4335
///delay for N microseconds
4436
/// # Arguments
45-
/// * 'ms' - an u32, number of microseconds to busy-wait
37+
/// * 'ms' - an u32, number of microseconds to busy-wait. This should be known at
38+
/// compile time, otherwise the delay may be much longer than specified.
4639
#[inline(always)]
4740
pub fn delay_us(us: u32) {
48-
// picoseconds
49-
let ps = us * 1000;
50-
let ps_lp = 1000000000 / (avr_config::CPU_FREQUENCY_HZ / 4);
51-
let loops = (ps / ps_lp) as u32;
52-
delay(loops);
53-
}
54-
55-
/// Internal function to implement a variable busy-wait loop.
56-
/// # Arguments
57-
/// * 'count' - a u32, the number of times to cycle the loop (4 clock cycles per loop).
58-
#[inline(always)]
59-
pub fn delay_count_32(count: u32) {
60-
let mut outer_count: u16 = (count >> 16) as u16;
61-
let inner_count: u16 = count as u16;
62-
if inner_count != 0 {
63-
delay_loop_4_cycles(inner_count);
64-
}
65-
while outer_count != 0 {
66-
delay_loop_4_cycles(0);
67-
outer_count -= 1;
68-
}
69-
}
70-
71-
/// Internal function to implement a variable busy-wait loop.
72-
/// # Arguments
73-
/// * 'count' - a u64, the number of times to cycle the loop (4 clock cycles per loop). *The top 16 bits are ignored.*
74-
#[inline(always)]
75-
pub fn delay_count_48(count: u64) {
76-
let mut outer_count: u32 = (count >> 16) as u32;
77-
let inner_count: u16 = count as u16;
78-
if inner_count != 0 {
79-
delay_loop_4_cycles(inner_count);
80-
}
81-
while outer_count != 0 {
82-
delay_loop_4_cycles(0);
83-
outer_count -= 1;
84-
}
85-
}
86-
87-
/// Internal function to implement a 16-bit busy-wait loop in assembly.
88-
/// Delays for 4 cycles per iteration, not including setup overhead.
89-
/// Up to 2^16 iterations (the value 2^16 would have to be passed as 0).
90-
/// # Arguments
91-
/// * 'cycles' - a u16, the number of times to cycle the loop.
92-
#[inline(always)]
93-
#[allow(unused_variables, unused_mut, unused_assignments, dead_code)]
94-
fn delay_loop_4_cycles(mut cycles: u16) {
95-
#[cfg(target_arch = "avr")]
96-
unsafe {
97-
asm!("1: sbiw {i}, 1",
98-
"brne 1b",
99-
i = inout(reg_iw) cycles => _,
100-
)
101-
}
102-
// Allow compilation even on non-avr targets, for testing purposes
103-
}
104-
105-
/// Internal function to implement an 8-bit busy-wait loop in assembly.
106-
/// Delays for 3 cycles per iteration, not including setup overhead.
107-
/// Up to 2^8 iterations (the value 2^8 would have to be passed as 0).
108-
/// # Arguments
109-
/// * 'cycles' - a u8, the number of times to cycle the loop.
110-
#[inline(always)]
111-
#[allow(unused_variables, unused_mut, unused_assignments, dead_code)]
112-
fn delay_loop_3_cycles(mut cycles: u8) {
113-
#[cfg(target_arch = "avr")]
114-
unsafe {
115-
asm!("1: dec {i}",
116-
"brne 1b",
117-
i = inout(reg) cycles => _,
118-
)
119-
}
41+
let ticks: u64 = (u64::from(avr_config::CPU_FREQUENCY_HZ) * u64::from(us)) / 4_000_000;
42+
delay_impl::delay_count_48(ticks);
12043
}
12144

12245
#[cfg(test)]

0 commit comments

Comments
 (0)