use crate::peripherals::Timer;
use crate::soc::ws63::SYSTEM_CLOCK_HZ;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerMode {
OneShot = 0,
Periodic = 1,
}
pub struct TimerDriver<'d> {
_timer: Timer<'d>,
}
impl<'d> TimerDriver<'d> {
pub fn new(timer: Timer<'d>) -> Self {
Self { _timer: timer }
}
fn regs(&self) -> &'static ws63_pac::timer::RegisterBlock {
unsafe { &*Timer::ptr() }
}
pub fn configure(&self, n: usize, mode: TimerMode, load_value: u32) {
let r = self.regs();
match n {
0 => r.timer0_load_count(0).write(|w| unsafe { w.bits(load_value) }),
1 => r.timer0_load_count(1).write(|w| unsafe { w.bits(load_value) }),
2 => r.timer0_load_count(2).write(|w| unsafe { w.bits(load_value) }),
_ => unreachable!(),
};
let ctl = ((mode as u32) & 0x3) << 1;
match n {
0 => r.timer0_control(0).write(|w| unsafe { w.bits(ctl) }),
1 => r.timer0_control(1).write(|w| unsafe { w.bits(ctl) }),
2 => r.timer0_control(2).write(|w| unsafe { w.bits(ctl) }),
_ => unreachable!(),
};
}
pub fn enable(&self, n: usize) {
let r = self.regs();
let prev = match n {
0 => r.timer0_control(0).read().bits(),
1 => r.timer0_control(1).read().bits(),
2 => r.timer0_control(2).read().bits(),
_ => unreachable!(),
};
match n {
0 => r.timer0_control(0).write(|w| unsafe { w.bits(prev | 1) }),
1 => r.timer0_control(1).write(|w| unsafe { w.bits(prev | 1) }),
2 => r.timer0_control(2).write(|w| unsafe { w.bits(prev | 1) }),
_ => unreachable!(),
};
}
pub fn disable(&self, n: usize) {
let r = self.regs();
let prev = match n {
0 => r.timer0_control(0).read().bits(),
1 => r.timer0_control(1).read().bits(),
2 => r.timer0_control(2).read().bits(),
_ => unreachable!(),
};
match n {
0 => r.timer0_control(0).write(|w| unsafe { w.bits(prev & !1) }),
1 => r.timer0_control(1).write(|w| unsafe { w.bits(prev & !1) }),
2 => r.timer0_control(2).write(|w| unsafe { w.bits(prev & !1) }),
_ => unreachable!(),
};
}
pub fn current_value(&self, n: usize) -> u32 {
let r = self.regs();
match n {
0 => r.timer0_current_value(0).read().bits(),
1 => r.timer0_current_value(1).read().bits(),
2 => r.timer0_current_value(2).read().bits(),
_ => unreachable!(),
}
}
pub fn interrupt_pending(&self, n: usize) -> bool {
let r = self.regs();
match n {
0 => r.timer0_raw_intr(0).read().bits() & 1 != 0,
1 => r.timer0_raw_intr(1).read().bits() & 1 != 0,
2 => r.timer0_raw_intr(2).read().bits() & 1 != 0,
_ => unreachable!(),
}
}
pub fn clear_interrupt(&self, n: usize) {
let r = self.regs();
match n {
0 => {
let _ = r.timer0_eoi(0).read().bits();
}
1 => {
let _ = r.timer0_eoi(1).read().bits();
}
2 => {
let _ = r.timer0_eoi(2).read().bits();
}
_ => unreachable!(),
}
}
pub fn oneshot(&self, channel: usize) -> OneShotTimer<'_> {
OneShotTimer { driver: self, channel }
}
pub fn periodic(&self, channel: usize) -> PeriodicTimer<'_> {
PeriodicTimer { driver: self, channel }
}
}
pub struct OneShotTimer<'a> {
driver: &'a TimerDriver<'a>,
channel: usize,
}
impl OneShotTimer<'_> {
pub fn start(&mut self, count: u32) {
self.driver.configure(self.channel, TimerMode::OneShot, count);
self.driver.enable(self.channel);
}
pub fn start_micros(&mut self, us: u32) {
let ticks64 = SYSTEM_CLOCK_HZ as u64 * us as u64 / 1_000_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
self.start(ticks);
}
pub fn start_millis(&mut self, ms: u32) {
let ticks64 = SYSTEM_CLOCK_HZ as u64 * ms as u64 / 1_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
self.start(ticks);
}
pub fn expired(&self) -> bool {
self.driver.interrupt_pending(self.channel)
}
pub fn wait(&self) {
while !self.expired() {}
self.driver.clear_interrupt(self.channel);
}
pub fn current(&self) -> u32 {
self.driver.current_value(self.channel)
}
pub fn stop(&self) {
self.driver.disable(self.channel);
}
pub fn clear(&self) {
self.driver.clear_interrupt(self.channel);
}
}
impl embedded_hal::delay::DelayNs for OneShotTimer<'_> {
fn delay_ns(&mut self, ns: u32) {
let ticks64 = (SYSTEM_CLOCK_HZ as u64 * ns as u64) / 1_000_000_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
if ticks > 0 {
self.start(ticks);
self.wait();
}
}
fn delay_us(&mut self, us: u32) {
self.start_micros(us);
self.wait();
}
fn delay_ms(&mut self, ms: u32) {
self.start_millis(ms);
self.wait();
}
}
pub struct PeriodicTimer<'a> {
driver: &'a TimerDriver<'a>,
channel: usize,
}
impl PeriodicTimer<'_> {
pub fn start(&mut self, period: u32) {
self.driver.configure(self.channel, TimerMode::Periodic, period);
self.driver.enable(self.channel);
}
pub fn start_micros(&mut self, us: u32) {
let ticks64 = SYSTEM_CLOCK_HZ as u64 * us as u64 / 1_000_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
self.start(ticks);
}
pub fn tick_elapsed(&self) -> bool {
self.driver.interrupt_pending(self.channel)
}
pub fn wait_tick(&self) {
while !self.tick_elapsed() {}
self.driver.clear_interrupt(self.channel);
}
pub fn stop(&self) {
self.driver.disable(self.channel);
}
pub fn current(&self) -> u32 {
self.driver.current_value(self.channel)
}
pub fn clear_tick(&self) {
self.driver.clear_interrupt(self.channel);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_oneshot_overflows_u32_clamps() {
let ticks64: u64 = 240_000_000u64 * 20_000_000u64 / 1_000_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
assert_eq!(ticks, u32::MAX);
}
#[test]
fn test_oneshot_small_value_does_not_clamp() {
let ticks64: u64 = 240_000_000u64 * 100u64 / 1_000_000;
assert_eq!(ticks64, 24_000);
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
assert_eq!(ticks, 24_000);
}
#[test]
fn test_oneshot_max_safe_value() {
let max_safe_us: u32 = u32::MAX / 240;
let ticks64: u64 = 240_000_000u64 * max_safe_us as u64 / 1_000_000;
assert!(ticks64 <= u32::MAX as u64);
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
assert_eq!(ticks, ticks64 as u32);
}
#[test]
fn test_periodic_clamping_matches_oneshot() {
let us: u32 = 20_000_000; let oneshot_ticks64: u64 = 240_000_000u64 * us as u64 / 1_000_000;
let periodic_ticks64: u64 = crate::soc::ws63::SYSTEM_CLOCK_HZ as u64 * us as u64 / 1_000_000;
assert_eq!(oneshot_ticks64, periodic_ticks64); let oneshot = if oneshot_ticks64 > u32::MAX as u64 { u32::MAX } else { oneshot_ticks64 as u32 };
let periodic = if periodic_ticks64 > u32::MAX as u64 { u32::MAX } else { periodic_ticks64 as u32 };
assert_eq!(oneshot, periodic);
assert_eq!(oneshot, u32::MAX);
}
}
#[cfg(test)]
mod proptests {
use proptest::prelude::*;
proptest! {
#[test]
fn timer_ticks_never_panics(us in any::<u32>()) {
let ticks64: u64 = 240_000_000u64 * us as u64 / 1_000_000;
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
let _ = ticks;
}
#[test]
fn timer_clamping_idempotent(us in any::<u32>()) {
let ticks64: u64 = 240_000_000u64 * us as u64 / 1_000_000;
let first = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
let second = if first as u64 > u32::MAX as u64 { u32::MAX } else { first };
prop_assert_eq!(first, second);
}
fn timer_safe_range_not_clamped(us in 0u32..17_895_697u32) {
let ticks64: u64 = 240_000_000u64 * us as u64 / 1_000_000;
prop_assert!(ticks64 <= u32::MAX as u64, "safe us={} produced ticks64={} > u32::MAX", us, ticks64);
}
fn timer_overflow_always_clamps(us in 17_895_698u32..u32::MAX) {
let ticks64: u64 = 240_000_000u64 * us as u64 / 1_000_000;
prop_assert!(ticks64 > u32::MAX as u64);
let ticks = if ticks64 > u32::MAX as u64 { u32::MAX } else { ticks64 as u32 };
prop_assert_eq!(ticks, u32::MAX);
}
#[test]
fn timer_both_formulas_equivalent(us in any::<u32>()) {
let oneshot: u64 = 240_000_000u64 * us as u64 / 1_000_000;
let periodic: u64 = crate::soc::ws63::SYSTEM_CLOCK_HZ as u64 * us as u64 / 1_000_000;
prop_assert_eq!(oneshot, periodic);
}
}
}