atsamd_hal/
sleeping_delay.rs

1//! Delays with WFI sleep while we wait using a timer
2use core::sync::atomic;
3use cortex_m::asm;
4use fugit::ExtU32;
5
6use crate::ehal::delay::DelayNs;
7use crate::ehal_02;
8use crate::timer_traits::InterruptDrivenTimer;
9
10const NUM_NS_IN_S: u32 = 1_000_000_000;
11
12/// Delay and sleep while we do (WFI) using a timer
13pub struct SleepingDelay<TIM> {
14    timer: TIM,
15    interrupt_fired: &'static atomic::AtomicBool,
16}
17
18impl<TIM> SleepingDelay<TIM>
19where
20    TIM: InterruptDrivenTimer,
21{
22    /// Initializes a new SleepingDelay struct
23    pub fn new(timer: TIM, interrupt_fired: &'static atomic::AtomicBool) -> SleepingDelay<TIM>
24    where
25        TIM: InterruptDrivenTimer,
26    {
27        SleepingDelay {
28            timer,
29            interrupt_fired,
30        }
31    }
32
33    /// Releases the timer resource
34    pub fn free(self) -> TIM {
35        self.timer
36    }
37}
38
39impl<TIM, TYPE> ehal_02::blocking::delay::DelayUs<TYPE> for SleepingDelay<TIM>
40where
41    TIM: InterruptDrivenTimer,
42    TYPE: Into<u32>,
43{
44    fn delay_us(&mut self, us: TYPE) {
45        <Self as DelayNs>::delay_us(self, us.into());
46    }
47}
48
49impl<TIM, TYPE> ehal_02::blocking::delay::DelayMs<TYPE> for SleepingDelay<TIM>
50where
51    TIM: InterruptDrivenTimer,
52    TYPE: Into<u32>,
53{
54    fn delay_ms(&mut self, ms: TYPE) {
55        <Self as DelayNs>::delay_ms(self, ms.into());
56    }
57}
58
59impl<TIM: InterruptDrivenTimer> DelayNs for SleepingDelay<TIM> {
60    fn delay_ns(&mut self, ns: u32) {
61        // Determine how many cycles we need to run for this delay, if any
62        // Avoid timers that run longer than a second because for 48 MHz-based timers,
63        //   there is no valid divisor + cycle count greater than ~1.3s, so we'd panic.
64        let mut loop_counter: u32 = 1 + (ns / NUM_NS_IN_S);
65
66        // Start the timer and sleep!
67        self.timer.start((ns / loop_counter).nanos());
68        self.timer.enable_interrupt();
69        loop {
70            asm::wfi();
71            if self.timer.wait().is_ok() || self.interrupt_fired.load(atomic::Ordering::Relaxed) {
72                self.interrupt_fired.store(false, atomic::Ordering::Relaxed);
73                loop_counter -= 1;
74                if loop_counter == 0 {
75                    break;
76                }
77            }
78        }
79        self.timer.disable_interrupt();
80    }
81}