kea_hal/system/pmc.rs
1//! # PMC - Power Management Controller
2//!
3//! The PMC controls the Low Voltage Detect and the ADC bandgap voltage
4//! reference functions.
5//!
6//! TODO: write an example
7//!
8//! ## Low Voltage Detect
9//!
10//! ### Low Voltage Detect Reset
11//!
12//! This feature, when enabled can trigger a reset when the MCU's input voltage
13//! drops below a selectable voltage threshold. This functionality can be
14//! active in both running and stop modes (default) or in running mode only.
15//!
16//! ### Low Voltage Warning
17//!
18//! This feature sets a warning flag bit and optional triggers an interrupt
19//! when the warning voltage threshold has been crossed. This threshold is
20//! user-selectable with 8 options grouped into sets of 4 which depend on which
21//! low voltage detect reset threshold is chosen.
22//!
23//! ## Bandgap Voltage Reference
24//!
25//! The PMC contains a field that enables the internal buffer for the
26//! bandgap voltage reference used by the ADC adn ACMP peripherals. This is
27//! impelemented in those areas. See [crate::adc::OnChipChannels::bandgap]
28
29use crate::{pac::PMC, HALExt};
30use core::marker::PhantomData;
31
32/// Error Enumeration
33#[repr(u8)]
34pub enum Error {
35 /// Somehow we accessed more bits than the field can hold
36 OutofBounds,
37}
38
39impl HALExt for PMC {
40 type T = Pmc<Enabled, _2v6>;
41 fn split(self) -> Pmc<Enabled, _2v6> {
42 Pmc {
43 peripheral: self,
44 _state: PhantomData,
45 _range: PhantomData,
46 lv_reset: LVReset {
47 _state: PhantomData,
48 },
49 lv_warn: LVWarn {
50 _state: PhantomData,
51 _range: PhantomData,
52 },
53 }
54 }
55}
56
57/// Datastructure for PMC interface
58pub struct Pmc<State, Range> {
59 peripheral: PMC,
60 // this state may need to be moved into the lv_reset interface if disabling
61 // LV Reset does not also disable the LV Warning.
62 _state: PhantomData<State>,
63 _range: PhantomData<Range>,
64 /// Controls when the Low Voltage Detection is active.
65 pub lv_reset: LVReset<RunAndStop>,
66 /// Interface for the Low Voltage Warning / Interrupt
67 pub lv_warn: LVWarn<Flag, Range>,
68}
69/// The current state doesn't matter for reasons.
70///
71/// State-type common to several things.
72pub struct DontCare;
73
74/// The function is Enabled.
75///
76/// State-type common to several things.
77pub struct Enabled;
78
79/// The function is Disabled.
80///
81/// State-type common to several things.
82pub struct Disabled;
83
84/// The low voltage warning is a flag only.
85///
86/// The flag must be polled to detect if the low voltage warning threshold has
87/// been crossed.
88pub struct Flag;
89
90/// The low voltage warning triggers an interrupt (and a flag).
91pub struct Interrupt;
92
93/// The low voltage detection (for reset) is active only while MCU is in a run
94/// mode
95pub struct RunOnly;
96
97/// The low voltage detection (for reset) is active while the MCU is in run and
98/// stop modes.
99pub struct RunAndStop;
100
101/// Interface that controls when the Low Voltage Detection is active
102pub struct LVReset<State> {
103 _state: PhantomData<State>,
104}
105
106/// Interface for the low voltage warning / interrupt
107pub struct LVWarn<State, Range> {
108 _state: PhantomData<State>,
109 _range: PhantomData<Range>,
110}
111
112/// V_LVDH threshold = 4.3V typ. for all KEA parts.
113pub struct _4v3;
114/// V_LVDL threshold = 2.61V typ. for all KEA parts
115pub struct _2v6;
116
117/// Low Voltage Warning Threshold, Low range (set by V_LVDx).
118///
119/// Threshold voltages verified same for all KEA parts
120#[repr(u8)]
121pub enum LvwLow {
122 /// LVW1
123 _2v7 = 0,
124 /// LVW2
125 _2v8 = 1,
126 /// LVW3
127 _2v9 = 2,
128 /// LVW4
129 _3v0 = 3,
130}
131
132/// Low Voltage Warning Threshold, High range (set by V_LVDx).
133///
134/// Threshold voltages verified same for all KEA parts
135#[repr(u8)]
136pub enum LvwHigh {
137 /// LVW1
138 _4v4 = 0,
139 /// LVW2
140 _4v5 = 1,
141 /// LVW3
142 _4v6 = 2,
143 /// LVW4
144 _4v7 = 3,
145}
146
147impl<Range> Pmc<Enabled, Range> {
148 /// Disable LVD logic and threshold checking
149 ///
150 /// This field can only be written once after reset, meaning that once
151 /// disabled, it cannot be re-enabled. It is automatically re-enabled on
152 /// reset.
153 pub fn into_disabled(self) -> Pmc<Disabled, Range> {
154 self.peripheral.spmsc1.modify(|_, w| w.lvde().clear_bit());
155 Pmc {
156 peripheral: self.peripheral,
157 _state: PhantomData,
158 _range: PhantomData,
159 lv_reset: self.lv_reset,
160 lv_warn: self.lv_warn,
161 }
162 }
163}
164
165impl Pmc<Enabled, _2v6> {
166 /// Shift to high range
167 pub fn into_4v3(self) -> Pmc<Enabled, _4v3> {
168 self.peripheral.spmsc2.modify(|_, w| w.lvdv().set_bit());
169 Pmc {
170 peripheral: self.peripheral,
171 _state: PhantomData,
172 _range: PhantomData,
173 lv_reset: self.lv_reset,
174 lv_warn: self.lv_warn.into_high_range(),
175 }
176 }
177}
178
179impl Pmc<Enabled, _4v3> {
180 /// Shift to low range
181 pub fn into_4v3(self) -> Pmc<Enabled, _2v6> {
182 self.peripheral.spmsc2.modify(|_, w| w.lvdv().set_bit());
183 Pmc {
184 peripheral: self.peripheral,
185 _state: PhantomData,
186 _range: PhantomData,
187 lv_reset: self.lv_reset,
188 lv_warn: self.lv_warn.into_low_range(),
189 }
190 }
191}
192
193impl<State, Range> LVWarn<State, Range> {
194 /// Set to high range
195 ///
196 /// Called by PMC range function. passes knowledge of the higher level
197 /// range here. There is probably a better way to do this
198 pub fn into_high_range(self) -> LVWarn<State, _4v3> {
199 LVWarn {
200 _state: PhantomData,
201 _range: PhantomData,
202 }
203 }
204 /// Set to low range
205 ///
206 /// Called by PMC range function. passes knowledge of the lower level
207 /// range here. There is probably a better way to do this
208 pub fn into_low_range(self) -> LVWarn<State, _2v6> {
209 LVWarn {
210 _state: PhantomData,
211 _range: PhantomData,
212 }
213 }
214
215 /// Check for warning
216 pub fn warning(&self) -> bool {
217 let pmc = unsafe { &(*PMC::ptr()) };
218 pmc.spmsc1.read().lvwf().bit()
219 }
220
221 /// Acknowledge warning
222 pub fn clear_warning(&self) {
223 unsafe { (*PMC::ptr()).spmsc1.modify(|_, w| w.lvwack().bit(true)) };
224 }
225}
226
227impl<Range> LVWarn<Flag, Range> {
228 /// Interrupt on Low Voltage Warning
229 pub fn into_interrupt(self) -> LVWarn<Interrupt, Range> {
230 unsafe { (*PMC::ptr()).spmsc1.modify(|_, w| w.lvwie().set_bit()) };
231 LVWarn {
232 _state: PhantomData,
233 _range: PhantomData,
234 }
235 }
236}
237
238impl<Range> LVWarn<Interrupt, Range> {
239 /// Disable Low Voltage Warning interrupt.
240 pub fn into_flag(self) -> LVWarn<Flag, Range> {
241 unsafe { (*PMC::ptr()).spmsc1.modify(|_, w| w.lvwie().clear_bit()) };
242 LVWarn {
243 _state: PhantomData,
244 _range: PhantomData,
245 }
246 }
247}
248
249impl<State> LVWarn<State, _2v6> {
250 /// adjust warning threshold
251 ///
252 /// The enum argument type is only valid in low range.
253 pub fn set_threshold(&self, threshold: LvwLow) {
254 let pmc = unsafe { &(*PMC::ptr()) };
255 pmc.spmsc2.modify(|_, w| w.lvwv().bits(threshold as u8));
256 }
257
258 /// get the current warning threshold
259 pub fn threshold(&self) -> Result<LvwLow, Error> {
260 let pmc = unsafe { &(*PMC::ptr()) };
261 match pmc.spmsc2.read().lvwv().bits() {
262 0 => Ok(LvwLow::_2v7),
263 1 => Ok(LvwLow::_2v8),
264 2 => Ok(LvwLow::_2v9),
265 3 => Ok(LvwLow::_3v0),
266 _ => Err(Error::OutofBounds),
267 }
268 }
269}
270
271impl<State> LVWarn<State, _4v3> {
272 /// adjust warning threshold
273 ///
274 /// The enum argument type is only valid in high range.
275 pub fn set_threshold(&self, threshold: LvwHigh) {
276 let pmc = unsafe { &(*PMC::ptr()) };
277 pmc.spmsc2.modify(|_, w| w.lvwv().bits(threshold as u8));
278 }
279
280 /// get the current warning threshold
281 pub fn threshold(&self) -> Result<LvwHigh, Error> {
282 let pmc = unsafe { &(*PMC::ptr()) };
283 match pmc.spmsc2.read().lvwv().bits() {
284 0 => Ok(LvwHigh::_4v4),
285 1 => Ok(LvwHigh::_4v5),
286 2 => Ok(LvwHigh::_4v6),
287 3 => Ok(LvwHigh::_4v7),
288 _ => Err(Error::OutofBounds),
289 }
290 }
291}
292
293impl LVReset<RunAndStop> {
294 /// Do not run the Low Voltage Detection Reset during stop mode
295 pub fn into_run_only(self) -> LVReset<RunOnly> {
296 let pmc = unsafe { &(*PMC::ptr()) };
297 pmc.spmsc1.modify(|_, w| w.lvdse().clear_bit());
298 LVReset {
299 _state: PhantomData,
300 }
301 }
302
303 /// Disable Reset upon Low Voltage Detection
304 ///
305 /// This can only be changed one time, and it defaults to enabled.
306 pub fn into_disabled(self) -> LVReset<Disabled> {
307 let pmc = unsafe { &(*PMC::ptr()) };
308 pmc.spmsc1.modify(|_, w| w.lvdre().clear_bit());
309 LVReset {
310 _state: PhantomData,
311 }
312 }
313}
314
315impl LVReset<RunOnly> {
316 /// Run the Low Voltage Detection Reset during stop mode
317 pub fn into_run_and_stop(self) -> LVReset<RunAndStop> {
318 let pmc = unsafe { &(*PMC::ptr()) };
319 pmc.spmsc1.modify(|_, w| w.lvdse().set_bit());
320 LVReset {
321 _state: PhantomData,
322 }
323 }
324
325 /// Disable Reset upon Low Voltage Detection
326 ///
327 /// This can only be changed one time, and it defaults to enabled.
328 pub fn into_disabled(self) -> LVReset<Disabled> {
329 let pmc = unsafe { &(*PMC::ptr()) };
330 pmc.spmsc1.modify(|_, w| w.lvdre().clear_bit());
331 LVReset {
332 _state: PhantomData,
333 }
334 }
335}