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}