lpc8xx_hal/
pmu.rs

1//! API for the Power Management Unit (PMU)
2//!
3//! The entry point to this API is [`PMU`]. Please refer to [`PMU`]'s
4//! documentation for additional information.
5//!
6//! The PMU is described in the user manual, chapter 6.
7//!
8//! # Examples
9//!
10//! Use the PMU to enter sleep mode:
11//!
12//! ``` no_run
13//! use lpc8xx_hal::{
14//!     Peripherals,
15//!     pac::CorePeripherals,
16//! };
17//!
18//! let mut cp = CorePeripherals::take().unwrap();
19//! let mut p = Peripherals::take().unwrap();
20//!
21//! let mut pmu = p.PMU.split();
22//!
23//! // Enters sleep mode. Unless we set up some interrupts, we won't wake up
24//! // from this again.
25//! pmu.handle.enter_sleep_mode(&mut cp.SCB);
26//! ```
27//!
28//! Please refer to the [examples in the repository] for more example code.
29//!
30//! [examples in the repository]: https://github.com/lpc-rs/lpc8xx-hal/tree/master/examples
31
32use cortex_m::{asm, interrupt};
33
34use crate::{clock, init_state, pac};
35
36/// Entry point to the PMU API
37///
38/// The PMU API is split into multiple parts, which are all available through
39/// [`pmu::Parts`]. You can use [`PMU::split`] to gain access to [`pmu::Parts`].
40///
41/// You can also use this struct to gain access to the raw peripheral using
42/// [`PMU::free`]. This is the main reason this struct exists, as it's no longer
43/// possible to do this after the API has been split.
44///
45/// Use [`Peripherals`] to gain access to an instance of this struct.
46///
47/// Please refer to the [module documentation] for more information.
48///
49/// [`pmu::Parts`]: struct.Parts.html
50/// [`Peripherals`]: ../struct.Peripherals.html
51/// [module documentation]: index.html
52pub struct PMU {
53    pmu: pac::PMU,
54}
55
56impl PMU {
57    pub(crate) fn new(pmu: pac::PMU) -> Self {
58        PMU { pmu }
59    }
60
61    /// Splits the PMU API into its component parts
62    ///
63    /// This is the regular way to access the PMU API. It exists as an explicit
64    /// step, as it's no longer possible to gain access to the raw peripheral
65    /// using [`PMU::free`] after you've called this method.
66    pub fn split(self) -> Parts {
67        Parts {
68            handle: Handle { pmu: self.pmu },
69            low_power_clock: LowPowerClock::new(),
70        }
71    }
72
73    /// Return the raw peripheral
74    ///
75    /// This method serves as an escape hatch from the HAL API. It returns the
76    /// raw peripheral, allowing you to do whatever you want with it, without
77    /// limitations imposed by the API.
78    ///
79    /// If you are using this method because a feature you need is missing from
80    /// the HAL API, please [open an issue] or, if an issue for your feature
81    /// request already exists, comment on the existing issue, so we can
82    /// prioritize it accordingly.
83    ///
84    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
85    pub fn free(self) -> pac::PMU {
86        self.pmu
87    }
88}
89
90/// The main API for the PMU peripheral
91///
92/// Provides access to all types that make up the PMU API. Please refer to the
93/// [module documentation] for more information.
94///
95/// [module documentation]: index.html
96pub struct Parts {
97    /// The handle to the PMU peripheral
98    pub handle: Handle,
99
100    /// The 10 kHz low-power clock
101    pub low_power_clock: LowPowerClock<init_state::Disabled>,
102}
103
104/// Handle to the PMU peripheral
105///
106/// This handle to the PMU peripheral provides access to the main part of the
107/// PMU API. It is also required by other parts of the API to synchronize access
108/// the the underlying registers, wherever this is required.
109///
110/// Please refer to the [module documentation] for more information about the
111/// PMU.
112///
113/// [module documentation]: index.html
114pub struct Handle {
115    pmu: pac::PMU,
116}
117
118impl Handle {
119    /// Enter sleep mode
120    ///
121    /// The microcontroller will wake up from sleep mode, if an NVIC-enabled
122    /// interrupt occurs. See user manual, section 6.7.4.3.
123    pub fn enter_sleep_mode(&mut self, scb: &mut pac::SCB) {
124        interrupt::free(|_| {
125            // Default power mode indicates active or sleep mode.
126            self.pmu.pcon.modify(|_, w| w.pm().default());
127
128            // The SLEEPDEEP bit must be cleared when entering regular sleep
129            // mode. See user manual, section 6.7.4.2.
130            scb.clear_sleepdeep();
131
132            asm::dsb();
133            asm::wfi();
134        })
135    }
136
137    /// Enter deep-sleep mode
138    ///
139    /// The microcontroller will wake up from deep-sleep mode, if an
140    /// NVIC-enabled interrupt occurs. See user manual, section 6.7.5.3.
141    ///
142    /// # Limitations
143    ///
144    /// According to the user manual, section 6.7.5.2, the IRC must be selected
145    /// as the main clock before entering deep-sleep mode.
146    ///
147    /// If you intend to wake up from this mode again, you need to configure the
148    /// STARTERP0 and STARTERP1 registers of the SYSCON appropriately. See user
149    /// manual, section 6.5.1.
150    ///
151    /// # Safety
152    ///
153    /// The configuration of various peripherals after wake-up is controlled by
154    /// the PDAWAKECFG register. If the configuration in that register doesn't
155    /// match the peripheral states in the HAL API, you can confuse the API into
156    /// believing that peripherals have a different state than they actually
157    /// have which can lead to all kinds of adverse consequences.
158    ///
159    /// Please make sure that the peripheral states configured in PDAWAKECFG
160    /// match the peripheral states as tracked by the API before calling this
161    /// method.
162    pub unsafe fn enter_deep_sleep_mode(&mut self, scb: &mut pac::SCB) {
163        interrupt::free(|_| {
164            self.pmu.pcon.modify(|_, w| w.pm().deep_sleep_mode());
165
166            // The SLEEPDEEP bit must be set for entering regular sleep mode.
167            // See user manual, section 6.7.5.2.
168            scb.set_sleepdeep();
169
170            asm::dsb();
171            asm::wfi();
172        })
173    }
174
175    /// Enter power-down mode
176    ///
177    /// The microcontroller will wake up from power-down mode, if an
178    /// NVIC-enabled interrupt occurs. See user manual, section 6.7.6.3.
179    ///
180    /// # Limitations
181    ///
182    /// According to the user manual, section 6.7.6.2, the IRC must be selected
183    /// as the main clock before entering deep-sleep mode.
184    ///
185    /// If you intend to wake up from this mode again, you need to configure the
186    /// STARTERP0 and STARTERP1 registers of the SYSCON appropriately. See user
187    /// manual, section 6.5.1.
188    ///
189    /// # Safety
190    ///
191    /// The configuration of various peripherals after wake-up is controlled by
192    /// the PDAWAKECFG register. If the configuration in that register doesn't
193    /// match the peripheral states in this API, you can confuse the API into
194    /// believing that peripherals have a different state than they actually
195    /// have which can lead to all kinds of adverse consequences.
196    ///
197    /// Please make sure that the peripheral states configured in PDAWAKECFG
198    /// match the peripheral states as tracked by the API before calling this
199    /// method.
200    pub unsafe fn enter_power_down_mode(&mut self, scb: &mut pac::SCB) {
201        interrupt::free(|_| {
202            self.pmu.pcon.modify(|_, w| w.pm().power_down_mode());
203
204            // The SLEEPDEEP bit must be set for entering regular sleep mode.
205            // See user manual, section 6.7.5.2.
206            scb.set_sleepdeep();
207
208            asm::dsb();
209            asm::wfi();
210        })
211    }
212}
213
214/// The 10 kHz low-power clock
215///
216/// This is one of the clocks that can be used to run the self-wake-up timer
217/// (WKT). See user manual, section 18.5.1.
218pub struct LowPowerClock<State = init_state::Enabled> {
219    _state: State,
220}
221
222impl LowPowerClock<init_state::Disabled> {
223    pub(crate) fn new() -> Self {
224        LowPowerClock {
225            _state: init_state::Disabled,
226        }
227    }
228}
229
230impl LowPowerClock<init_state::Disabled> {
231    /// Enable the low-power clock
232    ///
233    /// This method is only available, if `LowPowerClock` is in the [`Disabled`]
234    /// state. Code that attempts to call this method when the clock is already
235    /// enabled will not compile.
236    ///
237    /// Consumes this instance of `LowPowerClock` and returns another instance
238    /// that has its `State` type parameter set to [`Enabled`]. That new
239    /// instance implements [`clock::Enabled`], which might be required by APIs
240    /// that need an enabled clock.
241    ///
242    /// [`Disabled`]: ../init_state/struct.Disabled.html
243    /// [`Enabled`]: ../init_state/struct.Enabled.html
244    /// [`clock::Enabled`]: ../clock/trait.Enabled.html
245    pub fn enable(
246        self,
247        pmu: &mut Handle,
248    ) -> LowPowerClock<init_state::Enabled> {
249        pmu.pmu.dpdctrl.modify(|_, w| w.lposcen().enabled());
250
251        LowPowerClock {
252            _state: init_state::Enabled(()),
253        }
254    }
255}
256
257impl LowPowerClock<init_state::Enabled> {
258    /// Disable the low-power clock
259    ///
260    /// This method is only available, if `LowPowerClock` is in the [`Enabled`]
261    /// state. Code that attempts to call this method when the clock is already
262    /// disabled will not compile.
263    ///
264    /// Consumes this instance of `LowPowerClock` and returns another instance
265    /// that has its `State` type parameter set to [`Disabled`].
266    ///
267    /// [`Enabled`]: ../init_state/struct.Enabled.html
268    /// [`Disabled`]: ../init_state/struct.Disabled.html
269    pub fn disable(
270        self,
271        pmu: &mut Handle,
272    ) -> LowPowerClock<init_state::Disabled> {
273        pmu.pmu.dpdctrl.modify(|_, w| w.lposcen().disabled());
274
275        LowPowerClock {
276            _state: init_state::Disabled,
277        }
278    }
279}
280
281impl<State> clock::Frequency for LowPowerClock<State> {
282    fn hz(&self) -> u32 {
283        10_000
284    }
285}
286
287impl clock::Enabled for LowPowerClock<init_state::Enabled> {}