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> {}