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}