lpc8xx_hal/
wkt.rs

1//! API for the self-wake-up timer (WKT)
2//!
3//! The entry point to this API is [`WKT`].
4//!
5//! The WKT peripheral is described in the user manual, chapter 9.
6//!
7//! # Examples
8//!
9//! ``` no_run
10//! use lpc8xx_hal::{
11//!     prelude::*,
12//!     Peripherals,
13//! };
14//!
15//! let mut p = Peripherals::take().unwrap();
16//!
17//! let mut syscon = p.SYSCON.split();
18//! let mut timer  = p.WKT.enable(&mut syscon.handle);
19//!
20//! // Start the timer at 750000. Sine the IRC/FRO-derived clock runs at 750 kHz,
21//! // this translates to a one second wait.
22//! timer.start(750_000u32);
23//!
24//! while let Err(nb::Error::WouldBlock) = timer.wait() {
25//!     // do stuff
26//! }
27//! ```
28//!
29//! Please refer to the [examples in the repository] for more example code.
30//!
31//! [examples in the repository]: https://github.com/lpc-rs/lpc8xx-hal/tree/master/examples
32
33use embedded_hal::timer;
34use nb;
35use void::Void;
36
37use crate::{
38    init_state,
39    pac::{self, wkt::ctrl},
40    pmu::LowPowerClock,
41    syscon::{self, IoscDerivedClock},
42};
43
44/// Interface to the self-wake-up timer (WKT)
45///
46/// Controls the WKT. Use [`Peripherals`] to gain access to an instance of this
47/// struct.
48///
49/// Please refer to the [module documentation] for more information.
50///
51/// # `embedded-hal` traits
52/// - [`embedded_hal::timer::CountDown`]
53///
54/// [`Peripherals`]: ../struct.Peripherals.html
55/// [module documentation]: index.html
56/// [`embedded_hal::timer::CountDown`]: #impl-CountDown
57pub struct WKT<State = init_state::Enabled> {
58    wkt: pac::WKT,
59    _state: State,
60}
61
62impl WKT<init_state::Disabled> {
63    pub(crate) fn new(wkt: pac::WKT) -> Self {
64        WKT {
65            wkt,
66            _state: init_state::Disabled,
67        }
68    }
69
70    /// Enable the WKT
71    ///
72    /// This method is only available, if `WKT` is in the [`Disabled`] state.
73    /// Code that attempts to call this method when the peripheral is already
74    /// enabled will not compile.
75    ///
76    /// Consumes this instance of `WKT` and returns another instance that has
77    /// its `State` type parameter set to [`Enabled`].
78    ///
79    /// [`Disabled`]: ../init_state/struct.Disabled.html
80    /// [`Enabled`]: ../init_state/struct.Enabled.html
81    pub fn enable(
82        self,
83        syscon: &mut syscon::Handle,
84    ) -> WKT<init_state::Enabled> {
85        syscon.enable_clock(&self.wkt);
86
87        WKT {
88            wkt: self.wkt,
89            _state: init_state::Enabled(()),
90        }
91    }
92}
93
94impl WKT<init_state::Enabled> {
95    /// Disable the WKT
96    ///
97    /// This method is only available, if `WKT` is in the [`Enabled`] state.
98    /// Code that attempts to call this method when the peripheral is already
99    /// disabled will not compile.
100    ///
101    /// Consumes this instance of `WKT` and returns another instance that has
102    /// its `State` type parameter set to [`Disabled`].
103    ///
104    /// [`Enabled`]: ../init_state/struct.Enabled.html
105    /// [`Disabled`]: ../init_state/struct.Disabled.html
106    pub fn disable(
107        self,
108        syscon: &mut syscon::Handle,
109    ) -> WKT<init_state::Disabled> {
110        syscon.disable_clock(&self.wkt);
111
112        WKT {
113            wkt: self.wkt,
114            _state: init_state::Disabled,
115        }
116    }
117
118    /// Select the clock that runs the self-wake-up timer
119    ///
120    /// This method is only available if the WKT is enabled. Code attempting to
121    /// call this method when this is not the case will not compile.
122    ///
123    /// All clocks that can run the WKT implement a common trait. Please refer
124    /// to [`wkt::Clock`] for a list of clocks that can be passed to this
125    /// method. Selecting an external clock via the WKTCLKIN pin is currently
126    /// not supported.
127    ///
128    /// # Limitations
129    ///
130    /// Currently, nothing prevents the user from selecting a clock that is
131    /// disabled, attempting to start the timer while the clock is disabled, or
132    /// disabling the clock while the timer is running.
133    ///
134    /// [`wkt::Clock`]: trait.Clock.html
135    pub fn select_clock<C>(&mut self)
136    where
137        C: Clock,
138    {
139        self.wkt.ctrl.modify(|_, w| {
140            C::select(w);
141            w
142        });
143    }
144}
145
146impl timer::CountDown for WKT<init_state::Enabled> {
147    type Time = u32;
148
149    /// Starts a new count down
150    fn start<T>(&mut self, timeout: T)
151    where
152        T: Into<Self::Time>,
153    {
154        // Either clearing the counter or writing a value to it resets the alarm
155        // flag, so no reason to worry about that here.
156
157        // It's not allowed to write to the counter without clearing it first.
158        self.wkt.ctrl.modify(|_, w| w.clearctr().clear_bit());
159
160        // The counter has been cleared, which halts counting. Writing a new
161        // count is perfectly safe.
162        self.wkt
163            .count
164            .write(|w| unsafe { w.value().bits(timeout.into()) });
165    }
166
167    /// Non-blockingly "waits" until the count down finishes
168    fn wait(&mut self) -> nb::Result<(), Void> {
169        if self.wkt.ctrl.read().alarmflag().bit_is_set() {
170            return Ok(());
171        }
172
173        Err(nb::Error::WouldBlock)
174    }
175}
176
177impl<State> WKT<State> {
178    /// Return the raw peripheral
179    ///
180    /// This method serves as an escape hatch from the HAL API. It returns the
181    /// raw peripheral, allowing you to do whatever you want with it, without
182    /// limitations imposed by the API.
183    ///
184    /// If you are using this method because a feature you need is missing from
185    /// the HAL API, please [open an issue] or, if an issue for your feature
186    /// request already exists, comment on the existing issue, so we can
187    /// prioritize it accordingly.
188    ///
189    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
190    pub fn free(self) -> pac::WKT {
191        self.wkt
192    }
193}
194
195/// A clock that is usable by the self-wake-up timer (WKT)
196///
197/// This trait is implemented for all clocks that are supported by the WKT. The
198/// user shouldn't need to implement this trait themselves.
199pub trait Clock {
200    /// Internal method to select the clock as the clock source for the WKT
201    ///
202    /// This is an internal method, to be called by the WKT API. Users generally
203    /// shouldn't need to call this. This method is exempt from any guarantees
204    /// of API stability.
205    fn select(w: &mut ctrl::W);
206}
207
208impl<State> Clock for IoscDerivedClock<State> {
209    fn select(w: &mut ctrl::W) {
210        w.sel_extclk().internal();
211        target::select_internal_oscillator(w);
212    }
213}
214
215impl<State> Clock for LowPowerClock<State> {
216    fn select(w: &mut ctrl::W) {
217        w.sel_extclk().internal().clksel().low_power_clock();
218    }
219}
220
221#[cfg(feature = "82x")]
222mod target {
223    pub fn select_internal_oscillator(w: &mut crate::pac::wkt::ctrl::W) {
224        w.clksel().divided_irc_clock();
225    }
226}
227
228#[cfg(feature = "845")]
229mod target {
230    pub fn select_internal_oscillator(w: &mut crate::pac::wkt::ctrl::W) {
231        w.clksel().divided_fro_clock();
232    }
233}