-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
133 additions
and
52 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,121 @@ | ||
use core::sync::atomic::{AtomicU32, Ordering}; | ||
use core::time::Duration; | ||
use core::cell::RefCell; | ||
use core::ops::Add; | ||
|
||
use cortex_m::interrupt::{self, Mutex}; | ||
use cortex_m::peripheral::{SYST, syst::SystClkSource}; | ||
use cortex_m_rt::exception; | ||
|
||
use crate::Gcr; | ||
use crate::{Gcr, HalError}; | ||
|
||
const SYSTICK_RELOAD_VAL: u32 = 0xffffff; | ||
|
||
pub struct Timer { | ||
systick: SYST, | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||
pub struct Instant { | ||
time_since_boot: Duration, | ||
} | ||
|
||
impl Timer { | ||
pub(crate) fn new(mut systick: SYST) -> Self { | ||
systick.set_reload(SYSTICK_RELOAD_VAL); | ||
systick.clear_current(); | ||
systick.enable_counter(); | ||
systick.set_clock_source(SystClkSource::Core); | ||
impl Instant { | ||
pub fn now() -> Instant { | ||
let (current_tick, wrap_count) = interrupt::free(|token| { | ||
let mut systick_ref = SYSTICK.borrow(token).borrow_mut(); | ||
|
||
Timer { | ||
systick, | ||
} | ||
} | ||
let systick = systick_ref | ||
.as_mut() | ||
.expect("timer not initialized"); | ||
|
||
// first read current tick | ||
let current_tick = SYST::get_current(); | ||
|
||
pub fn sleep(&mut self, duration: Duration) { | ||
// NOTE: this systick thing counts down, not up | ||
let start_tick = SYST::get_current(); | ||
if systick.has_wrapped() { | ||
// a wrap has occured, use tick count of 0 and new value for wrap count | ||
let wrap_count = WRAP_COUNT.fetch_add(1, Ordering::Relaxed) + 1; | ||
|
||
(current_tick, wrap_count) | ||
} else { | ||
// no wrap occured, current tick count and wrap count are accurate | ||
(current_tick, WRAP_COUNT.load(Ordering::Relaxed)) | ||
} | ||
}); | ||
|
||
// current tick subtracted from reaload val because it counts down | ||
let total_ticks = (wrap_count as u64 * SYSTICK_RELOAD_VAL as u64) + (SYSTICK_RELOAD_VAL - current_tick) as u64; | ||
|
||
let usecs = duration.as_micros() as u64; | ||
let sysclock_freq = Gcr::with(|gcr| gcr.get_sysclock_frequency()) as u64; | ||
|
||
// total number of ticks we need to wait for | ||
let delay_ticks = ((usecs * sysclock_freq) / 1000000) as u32; | ||
// calculate seconds and microseconds seperately to avoid | ||
// potential overflow when ticks are multiplied by 1_000_000 | ||
let seconds = total_ticks / sysclock_freq; | ||
let remaining_ticks = total_ticks % sysclock_freq; | ||
|
||
let remaining_microseconds = (remaining_ticks * 1_000_000) / sysclock_freq; | ||
let total_microseconds = (seconds * 1_000_000) + remaining_microseconds; | ||
|
||
let mut wrap_count = delay_ticks / SYSTICK_RELOAD_VAL; | ||
let tick_count = delay_ticks % SYSTICK_RELOAD_VAL; | ||
Instant { | ||
time_since_boot: Duration::from_micros(total_microseconds), | ||
} | ||
} | ||
} | ||
|
||
let end_tick = if tick_count > start_tick { | ||
wrap_count += 1; | ||
(SYSTICK_RELOAD_VAL - tick_count) + start_tick | ||
} else { | ||
start_tick - tick_count | ||
}; | ||
impl Add<Duration> for Instant { | ||
type Output = Instant; | ||
|
||
while wrap_count > 0 { | ||
if self.systick.has_wrapped() { | ||
wrap_count -= 1; | ||
} | ||
fn add(self, rhs: Duration) -> Instant { | ||
Instant { | ||
time_since_boot: self.time_since_boot + rhs, | ||
} | ||
} | ||
} | ||
|
||
while SYST::get_current() > end_tick && !self.systick.has_wrapped() {} | ||
pub fn sleep(duration: Duration) { | ||
let start = Instant::now(); | ||
let end = start + duration; | ||
|
||
while Instant::now() < end {} | ||
} | ||
|
||
/// Runs the function, returns the result or none if it took too long | ||
pub fn timeout<T>(f: impl FnOnce() -> T, timeout_len: Duration) -> Result<T, HalError> { | ||
let start = Instant::now(); | ||
let result = f(); | ||
let end = Instant::now(); | ||
|
||
if start + timeout_len < end { | ||
Err(HalError::Timeout) | ||
} else { | ||
Ok(result) | ||
} | ||
} | ||
|
||
/// Initializes the timer | ||
pub(crate) fn init(mut systick: SYST) { | ||
interrupt::free(|token| { | ||
systick.set_reload(SYSTICK_RELOAD_VAL); | ||
systick.clear_current(); | ||
systick.enable_counter(); | ||
systick.set_clock_source(SystClkSource::Core); | ||
systick.enable_interrupt(); | ||
|
||
let mut systick_ref = SYSTICK.borrow(token).borrow_mut(); | ||
*systick_ref = Some(systick); | ||
}) | ||
} | ||
|
||
static SYSTICK: Mutex<RefCell<Option<SYST>>> = Mutex::new(RefCell::new(None)); | ||
static WRAP_COUNT: AtomicU32 = AtomicU32::new(0); | ||
|
||
#[exception] | ||
fn SysTick() { | ||
interrupt::free(|token| { | ||
let mut systick_ref = SYSTICK.borrow(token).borrow_mut(); | ||
|
||
let systick = systick_ref | ||
.as_mut() | ||
.expect("timer not initialized"); | ||
|
||
if systick.has_wrapped() { | ||
WRAP_COUNT.fetch_add(1, Ordering::Relaxed); | ||
} | ||
}) | ||
} |