Skip to content

Rewrote library to fix several issues #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/delay_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#[allow(unused_imports)]
use core::arch::asm;

/// Internal function to implement a variable busy-wait loop.
/// # Arguments
/// * 'count' - a u32, the number of times to cycle the loop (4 clock cycles per loop).
#[inline(always)]
pub fn delay_count_32(count: u32) {
let mut outer_count: u16 = (count >> 16) as u16;
let inner_count: u16 = count as u16;
if inner_count != 0 {
delay_loop_4_cycles(inner_count);
}
while outer_count != 0 {
delay_loop_4_cycles(0);
outer_count -= 1;
}
}

/// Internal function to implement a variable busy-wait loop.
/// # Arguments
/// * 'count' - a u64, the number of times to cycle the loop (4 clock cycles per loop). *The top 16 bits are ignored.*
#[inline(always)]
pub fn delay_count_48(count: u64) {
let mut outer_count: u32 = (count >> 16) as u32;
let inner_count: u16 = count as u16;
if inner_count != 0 {
delay_loop_4_cycles(inner_count);
}
while outer_count != 0 {
delay_loop_4_cycles(0);
outer_count -= 1;
}
}

/// Internal function to implement a 16-bit busy-wait loop in assembly.
/// Delays for 4 cycles per iteration, not including setup overhead.
/// Up to 2^16 iterations (the value 2^16 would have to be passed as 0).
/// # Arguments
/// * 'cycles' - a u16, the number of times to cycle the loop.
#[inline(always)]
#[allow(unused_variables, unused_mut, unused_assignments, dead_code)]
pub fn delay_loop_4_cycles(mut cycles: u16) {
#[cfg(target_arch = "avr")]
unsafe {
asm!("1: sbiw {i}, 1",
"brne 1b",
i = inout(reg_iw) cycles => _,
)
}
// Allow compilation even on non-avr targets, for testing purposes
}
80 changes: 28 additions & 52 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#![feature(asm_experimental_arch)]

#![no_std]

#![crate_name = "avr_delay"]
#![feature(asm_experimental_arch)]

use core::arch::asm;
mod delay_impl;

/// This library is intended to provide a busy-wait delay
/// similar to the one provided by the arduino c++ utilities
Expand All @@ -14,67 +12,45 @@ use core::arch::asm;
// This library does all of the busy-wait loop in rust.
// We pack as much of the looping as possible into asm!
// so that we can count cycles.
//
// Ignoring the overhead, which may be significant:
// An arduino runs at 16MHZ. Each asm loop is 4 cycles.
// so each loop is 0.25 us.
//
// the overhead of delay() seems to be about 13 cycles
// initially, and then 11 cycles per outer loop. We ignore
// all that.

/// Internal function to implement a variable busy-wait loop.
/// Internal function to implement a variable busy-wait loop. Even if count isn't
/// known at compile time, this function shouldn't have too much overhead.
/// # Arguments
/// * 'count' - a u64, the number of times to cycle the loop.
/// * 'count' - a u32, the number of times to cycle the loop.
#[inline(always)]
pub fn delay(count: u64) {
// Our asm busy-wait takes a 16 bit word as an argument,
// so the max number of loops is 2^16
let outer_count = count / 65536;
let last_count = ((count % 65536)+1) as u16;
for _ in 0..outer_count {
// Each loop through should be 4 cycles.
let zero = 0u16;
unsafe {
asm!("1: sbiw {i}, 1",
"brne 1b",
i = inout(reg_iw) zero => _,
)
}
}
unsafe {
asm!("1: sbiw {i}, 1",
"brne 1b",
i = inout(reg_iw) last_count => _,
)
}
pub fn delay(count: u32) {
delay_impl::delay_count_32(count);
}

///delay for N milliseconds
/// # Arguments
/// * 'ms' - an u64, number of milliseconds to busy-wait
/// * 'ms' - a u32, number of milliseconds to busy-wait. This should be known at
/// compile time, otherwise the delay may be much longer than specified.
#[inline(always)]
pub fn delay_ms(ms: u64) {
// microseconds
let us = ms * 1000;
delay_us(us);
pub fn delay_ms(ms: u32) {
const GCD: u32 = gcd(avr_config::CPU_FREQUENCY_HZ, 4_000);
const NUMERATOR: u32 = avr_config::CPU_FREQUENCY_HZ / GCD;
const DENOMINATOR: u32 = 4_000 / GCD;
let ticks: u64 = (u64::from(ms) * u64::from(NUMERATOR)) / u64::from(DENOMINATOR);
delay_impl::delay_count_48(ticks);
}

///delay for N microseconds
/// # Arguments
/// * 'us' - an u64, number of microseconds to busy-wait
/// * 'ms' - a u32, number of microseconds to busy-wait. This should be known at
/// compile time, otherwise the delay may be much longer than specified.
#[inline(always)]
pub fn delay_us(us: u64) {
let us_in_loop = (avr_config::CPU_FREQUENCY_HZ / 1000000 / 4) as u64;
let loops = us * us_in_loop;
delay(loops);
pub fn delay_us(us: u32) {
const GCD: u32 = gcd(avr_config::CPU_FREQUENCY_HZ, 4_000_000);
const NUMERATOR: u32 = avr_config::CPU_FREQUENCY_HZ / GCD;
const DENOMINATOR: u32 = 4_000_000 / GCD;
let ticks: u64 = (u64::from(us) * u64::from(NUMERATOR)) / u64::from(DENOMINATOR);
delay_impl::delay_count_48(ticks);
}

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
const fn gcd(mut a: u32, mut b: u32) -> u32 {
while b != 0 {
(a, b) = (b, a % b);
}
}

return a;
}