use crate::peripherals::Timer;
use crate::soc::ws63::TIMER_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 = TIMER_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 = TIMER_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 = (TIMER_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 = TIMER_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 crate::soc::ws63::TIMER_CLOCK_HZ;
const TICKS_PER_US: u64 = TIMER_CLOCK_HZ as u64 / 1_000_000;
const MAX_SAFE_US: u64 = u32::MAX as u64 / TICKS_PER_US;
fn ticks_for_us(us: u64) -> u32 {
let t = TIMER_CLOCK_HZ as u64 * us / 1_000_000;
if t > u32::MAX as u64 { u32::MAX } else { t as u32 }
}
#[test]
fn oneshot_overflow_clamps() {
assert_eq!(ticks_for_us(MAX_SAFE_US + 1_000_000), u32::MAX);
}
#[test]
fn small_value_does_not_clamp() {
assert_eq!(ticks_for_us(100), (100 * TICKS_PER_US) as u32);
}
#[test]
fn max_safe_value_not_clamped() {
let ticks64 = TIMER_CLOCK_HZ as u64 * MAX_SAFE_US / 1_000_000;
assert!(ticks64 <= u32::MAX as u64);
assert_eq!(ticks_for_us(MAX_SAFE_US), ticks64 as u32);
}
}
#[cfg(test)]
mod proptests {
use crate::soc::ws63::TIMER_CLOCK_HZ;
use proptest::prelude::*;
const MAX_SAFE_US: u64 = u32::MAX as u64 / (TIMER_CLOCK_HZ as u64 / 1_000_000);
fn ticks64(us: u64) -> u64 {
TIMER_CLOCK_HZ as u64 * us / 1_000_000
}
proptest! {
#[test]
fn ticks_never_panics(us in any::<u32>()) {
let t = ticks64(us as u64);
let _ = if t > u32::MAX as u64 { u32::MAX } else { t as u32 };
}
#[test]
fn safe_range_not_clamped(us in 0u64..=MAX_SAFE_US) {
prop_assert!(ticks64(us) <= u32::MAX as u64, "safe us={} -> ticks64={}", us, ticks64(us));
}
#[test]
fn overflow_always_clamps(us in (MAX_SAFE_US + 1)..=u32::MAX as u64) {
prop_assert!(ticks64(us) > u32::MAX as u64);
}
}
}
#[cfg(feature = "async")]
mod asynch_impl {
use super::{TIMER_CLOCK_HZ, Timer, TimerDriver, TimerMode};
use crate::asynch::IrqSignal;
use crate::interrupt::{self, Interrupt};
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
static TIMER_SIGNAL: [IrqSignal; 3] = [IrqSignal::new(), IrqSignal::new(), IrqSignal::new()];
fn ch_irq(ch: usize) -> Interrupt {
match ch {
0 => Interrupt::TIMER_INT0,
1 => Interrupt::TIMER_INT1,
_ => Interrupt::TIMER_INT2,
}
}
pub fn on_interrupt(ch: usize) {
let r = unsafe { &*Timer::ptr() };
match ch {
0 => {
let prev = r.timer0_control(0).read().bits();
r.timer0_control(0).write(|w| unsafe { w.bits(prev & !1) }); let _ = r.timer0_eoi(0).read().bits(); }
1 => {
let prev = r.timer0_control(1).read().bits();
r.timer0_control(1).write(|w| unsafe { w.bits(prev & !1) });
let _ = r.timer0_eoi(1).read().bits();
}
_ => {
let prev = r.timer0_control(2).read().bits();
r.timer0_control(2).write(|w| unsafe { w.bits(prev & !1) });
let _ = r.timer0_eoi(2).read().bits();
}
}
TIMER_SIGNAL[ch].signal();
}
pub struct AsyncDelay<'d> {
driver: TimerDriver<'d>,
channel: usize,
}
impl<'d> AsyncDelay<'d> {
pub fn new(timer: Timer<'d>, channel: usize) -> Self {
Self { driver: TimerDriver::new(timer), channel }
}
async fn delay_ticks(&mut self, ticks: u32) {
let ch = self.channel;
TIMER_SIGNAL[ch].reset();
self.driver.clear_interrupt(ch);
self.driver.configure(ch, TimerMode::OneShot, ticks.max(1));
unsafe { interrupt::enable(ch_irq(ch)) };
self.driver.enable(ch);
DelayFuture { ch }.await;
}
}
struct DelayFuture {
ch: usize,
}
impl Future for DelayFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if TIMER_SIGNAL[self.ch].take_fired() {
Poll::Ready(())
} else {
TIMER_SIGNAL[self.ch].register(cx.waker());
Poll::Pending
}
}
}
impl embedded_hal_async::delay::DelayNs for AsyncDelay<'_> {
async fn delay_ns(&mut self, ns: u32) {
let ticks = (TIMER_CLOCK_HZ as u64 * ns as u64 / 1_000_000_000) as u32;
self.delay_ticks(ticks).await;
}
}
}
#[cfg(feature = "async")]
pub use asynch_impl::{AsyncDelay, on_interrupt};