lpc8xx_hal/sleep.rs
1//! Higher-level sleep API
2//!
3//! This module provides a higher-level API layer that can be used to put the
4//! microcontroller to sleep for a given amount of time.
5//!
6//! Both sleeping via busy waiting and via regular sleep mode are supported.
7//! Please refer to [`sleep::Busy`] and [`sleep::Regular`] for more details.
8//!
9//! [`sleep::Busy`]: struct.Busy.html
10//! [`sleep::Regular`]: struct.Regular.html
11
12use cortex_m::{asm, interrupt};
13use embedded_hal::prelude::*;
14use nb;
15
16use crate::{
17 clock::{self, Ticks},
18 pac::{self, Interrupt, NVIC},
19 pmu,
20 wkt::{self, WKT},
21};
22
23/// Trait for putting the processor to sleep
24///
25/// There will typically one implementation of `Sleep` per sleep mode that is
26/// available on a given microcontroller.
27pub trait Sleep<Clock>
28where
29 Clock: clock::Enabled,
30{
31 /// Puts the processor to sleep for the given number of ticks of the clock
32 fn sleep<'clock, T>(&mut self, ticks: T)
33 where
34 Clock: 'clock,
35 T: Into<Ticks<'clock, Clock>>;
36}
37
38/// Sleep mode based on busy waiting
39///
40/// Provides a [`Sleep`] implementation based on busy waiting and uses the [WKT]
41/// to measure the time. An interrupt handler is not required.
42///
43/// Only clocks that the WKT supports can be used. See [`wkt::Clock`] for more
44/// details.
45///
46/// Since this sleep mode waits busily, which is very energy-inefficient, it is
47/// only suitable for simple examples, or very short wait times.
48///
49/// # Examples
50///
51/// ``` no_run
52/// use lpc8xx_hal::{
53/// prelude::*,
54/// Peripherals,
55/// clock::Ticks,
56/// sleep,
57/// };
58///
59/// let mut p = Peripherals::take().unwrap();
60///
61/// let mut syscon = p.SYSCON.split();
62/// let mut wkt = p.WKT.enable(&mut syscon.handle);
63///
64/// let clock = syscon.iosc_derived_clock;
65///
66/// let mut sleep = sleep::Busy::prepare(&mut wkt);
67///
68/// let delay = Ticks { value: 750_000, clock: &clock }; // 1000 ms
69/// sleep.sleep(delay);
70/// ```
71pub struct Busy<'wkt> {
72 wkt: &'wkt mut WKT,
73}
74
75impl<'wkt> Busy<'wkt> {
76 /// Prepare busy sleep mode
77 ///
78 /// Returns an instance of `sleep::Busy`, which implements [`Sleep`] and can
79 /// therefore be used to put the microcontroller to sleep.
80 ///
81 /// Requires a mutable reference to [`WKT`]. The reference will be borrowed
82 /// for as long as the `sleep::Busy` instance exists, as it will be needed
83 /// to count down the time in every call to [`Sleep::sleep`].
84 pub fn prepare(wkt: &'wkt mut WKT) -> Self {
85 Busy { wkt }
86 }
87}
88
89impl<'wkt, Clock> Sleep<Clock> for Busy<'wkt>
90where
91 Clock: clock::Enabled + wkt::Clock,
92{
93 fn sleep<'clock, T>(&mut self, ticks: T)
94 where
95 Clock: 'clock,
96 T: Into<Ticks<'clock, Clock>>,
97 {
98 let ticks: Ticks<Clock> = ticks.into();
99
100 // If we try to sleep for zero cycles, we'll never wake up again.
101 if ticks.value == 0 {
102 return;
103 }
104
105 self.wkt.start(ticks.value);
106 while let Err(nb::Error::WouldBlock) = self.wkt.wait() {
107 asm::nop();
108 }
109 }
110}
111
112/// Regular sleep mode
113///
114/// Provides a [`Sleep`] implementation for the regular sleep mode and uses the
115/// [WKT] to wake the microcontroller up again, at the right time. Only clocks
116/// that the WKT supports can be used. See [`wkt::Clock`] for more details.
117///
118/// # Examples
119///
120/// ``` no_run
121/// use lpc8xx_hal::{
122/// prelude::*,
123/// Peripherals,
124/// clock::Ticks,
125/// pac::CorePeripherals,
126/// sleep,
127/// };
128///
129/// let mut cp = CorePeripherals::take().unwrap();
130/// let mut p = Peripherals::take().unwrap();
131///
132/// let mut pmu = p.PMU.split();
133/// let mut syscon = p.SYSCON.split();
134/// let mut wkt = p.WKT.enable(&mut syscon.handle);
135///
136/// let clock = syscon.iosc_derived_clock;
137///
138/// let mut sleep = sleep::Regular::prepare(
139/// &mut pmu.handle,
140/// &mut cp.SCB,
141/// &mut wkt,
142/// );
143///
144/// let delay = Ticks { value: 750_000, clock: &clock }; // 1000 ms
145///
146/// // This will put the microcontroller into sleep mode.
147/// sleep.sleep(delay);
148/// ```
149pub struct Regular<'r> {
150 pmu: &'r mut pmu::Handle,
151 scb: &'r mut pac::SCB,
152 wkt: &'r mut WKT,
153}
154
155impl<'r> Regular<'r> {
156 /// Prepare regular sleep mode
157 ///
158 /// Returns an instance of `sleep::Regular`, which implements [`Sleep`] and
159 /// can therefore be used to put the microcontroller to sleep.
160 ///
161 /// Requires references to various peripherals, which will be borrowed for
162 /// as long as the `sleep::Regular` instance exists, as they will be needed
163 /// for every call to [`Sleep::sleep`].
164 pub fn prepare(
165 pmu: &'r mut pmu::Handle,
166 scb: &'r mut pac::SCB,
167 wkt: &'r mut WKT,
168 ) -> Self {
169 Regular { pmu, scb, wkt }
170 }
171}
172
173impl<'r, Clock> Sleep<Clock> for Regular<'r>
174where
175 Clock: clock::Enabled + wkt::Clock,
176{
177 fn sleep<'clock, T>(&mut self, ticks: T)
178 where
179 Clock: 'clock,
180 T: Into<Ticks<'clock, Clock>>,
181 {
182 let ticks: Ticks<Clock> = ticks.into();
183
184 // If we try to sleep for zero cycles, we'll never wake up again.
185 if ticks.value == 0 {
186 return;
187 }
188
189 self.wkt.select_clock::<Clock>();
190 self.wkt.start(ticks.value);
191
192 // Within the this closure, interrupts are enabled, but interrupt
193 // handlers won't run. This means that we'll exit sleep mode when the
194 // WKT interrupt is fired, but there won't be an interrupt handler that
195 // will require the WKT's alarm flag to be reset. This means the `wait`
196 // method can use the alarm flag, which would otherwise need to be reset
197 // to exit the interrupt handler.
198 interrupt::free(|_| {
199 // Safe, because this is not going to interfere with the critical
200 // section.
201 unsafe { NVIC::unmask(Interrupt::WKT) };
202
203 while let Err(nb::Error::WouldBlock) = self.wkt.wait() {
204 self.pmu.enter_sleep_mode(self.scb);
205 }
206
207 // If we don't do this, the (possibly non-existing) interrupt
208 // handler will be called as soon as we exit this closure.
209 NVIC::mask(Interrupt::WKT);
210 });
211 }
212}