Skip to content

Instantly share code, notes, and snippets.

@diondokter
Created April 29, 2021 20:05
Show Gist options
  • Save diondokter/4b7bd8b84c5d434c6be065201fa0be22 to your computer and use it in GitHub Desktop.
Save diondokter/4b7bd8b84c5d434c6be065201fa0be22 to your computer and use it in GitHub Desktop.
pub struct LocalTimer {
local_timer: TIM5,
overflows: u32,
next_compare_value: Option<u64>,
}
impl LocalTimer {
const CLOCK_SPEED: u32 = 240_000_000;
const CLOCK_DIVIDER: u16 = 1;
const TIMER_SPEED: u32 = Self::CLOCK_SPEED / Self::CLOCK_DIVIDER as u32;
const TIMER_MAX: u32 = u32::MAX;
pub fn start(local_timer: TIM5) -> Self {
assert_eq!(
TIM5::get_clk(&get_clocks()),
Some(Self::CLOCK_SPEED.hz()),
"Settings have changed. Change these numbers and also the fraction in the clock impl"
);
// Set prescaler divide by the given amount
local_timer
.psc
.write(|w| w.psc().bits(Self::CLOCK_DIVIDER - 1));
// Set auto reload to max
local_timer.arr.write(|w| w.arr().bits(Self::TIMER_MAX));
// Reset the counter
local_timer.cnt.reset();
// Set Update Request Source to counter overflow/underflow only
local_timer.cr1.modify(|_, w| w.urs().counter_only());
// Force an update of the psc and arr registers
local_timer.egr.write(|w| w.ug().update());
// Trigger interrupt on updates (so now, that's only counter overflows)
local_timer.dier.write(|w| w.uie().enabled());
// Start the timer
local_timer.cr1.modify(|_, w| w.cen().set_bit());
Self {
local_timer,
overflows: 0,
next_compare_value: None,
}
}
/// Try to set the compare value.
///
/// Return true if it was set
fn try_set_compare_at(&mut self, ticks: u64) -> bool {
let tick_overflows = (ticks >> 32) as u32;
let tick_ticks = (ticks & 0xFFFF_FFFF) as u32;
let timer_ticks = self.local_timer.cnt.read().bits();
if tick_overflows < self.overflows {
// In the past, so trigger immediately
self.local_timer.egr.write(|w| w.cc1g().trigger());
self.local_timer.dier.modify(|_, w| w.cc1ie().enabled());
true
} else if tick_overflows == self.overflows && tick_ticks < timer_ticks {
// In the past, so trigger immediately
self.local_timer.egr.write(|w| w.cc1g().trigger());
self.local_timer.dier.modify(|_, w| w.cc1ie().enabled());
true
} else if tick_overflows == self.overflows {
// In the future of the current overflow
self.local_timer.ccr1.write(|w| w.ccr().bits(tick_ticks));
self.local_timer.dier.modify(|_, w| w.cc1ie().enabled());
true
} else {
// In the future after overflow
self.local_timer.dier.modify(|_, w| w.cc1ie().disabled());
false
}
}
}
impl Clock for LocalTimer {
type T = u64;
const SCALING_FACTOR: rtic::rtic_monotonic::Fraction =
rtic::rtic_monotonic::Fraction::new(1, Self::TIMER_SPEED);
fn try_now(&self) -> Result<rtic::rtic_monotonic::Instant<Self>, rtic::time::clock::Error> {
let counter = self.local_timer.cnt.read().bits();
let has_overflowed = self.local_timer.sr.read().uif().is_update_pending();
Ok(rtic::rtic_monotonic::Instant::new(
((self.overflows as u64 + if has_overflowed { 1 } else { 0 }) << 32) | (counter as u64),
))
}
}
impl Monotonic for LocalTimer {
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
unsafe fn reset(&mut self) {
self.local_timer.cnt.reset();
self.local_timer.sr.modify(|_, w| w.uif().clear_bit());
let _ = self.local_timer.sr.read();
let _ = self.local_timer.sr.read(); // Delay 2 peripheral clocks
}
fn set_compare(&mut self, instant: &rtic::rtic_monotonic::Instant<Self>) {
let instant_ticks = *instant.duration_since_epoch().integer();
if !self.try_set_compare_at(instant_ticks) {
self.next_compare_value = Some(instant_ticks);
}
}
fn clear_compare_flag(&mut self) {
self.local_timer.dier.modify(|_, w| w.cc1ie().disabled());
self.local_timer.sr.modify(|_, w| w.cc1if().clear_bit());
self.next_compare_value = None;
}
fn on_interrupt(&mut self) {
let sr = self.local_timer.sr.read();
if sr.uif().is_update_pending() {
// Overflowed
self.local_timer.sr.modify(|_, w| {
// Clears timeout event
w.uif().clear_bit()
});
let _ = self.local_timer.sr.read();
let _ = self.local_timer.sr.read(); // Delay 2 peripheral clocks
self.overflows += 1;
if let Some(compare_value) = self.next_compare_value {
if self.try_set_compare_at(compare_value) {
self.next_compare_value = None;
}
}
}
if sr.cc1if().bit_is_set() {
// Compared
self.clear_compare_flag();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment