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}