Skip to main content

imxrt_hal/chip/drivers/
tempmon.rs

1//! Temperature monitor.
2//!
3//! ## IMPORTANT NOTE:
4//!
5//! On 10xx MCUs, the temperature sensor uses and assumes that the bandgap
6//! reference, 480MHz PLL and 32KHz RTC modules are properly programmed and fully
7//! settled for correct operation.
8//!
9//! ## Example 1
10//!
11//! Manually triggered read
12//!
13//! ```no_run
14//! use core::task::Poll;
15//! use imxrt_hal as hal;
16//! use imxrt_ral as ral;
17//!
18//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
19//! let mut temp_mon = hal::tempmon::TempMon::new(inst);
20//! loop {
21//!     if let Poll::Ready(Ok(temperature)) = temp_mon.measure_temp() {
22//!         // Temperature in mC (1°C = 1000°mC)
23//!     }
24//! }
25//! ```
26//!
27//! ## Example 2
28//!
29//! Non-blocking reading
30//!
31//! ```no_run
32//! use imxrt_hal::tempmon::TempMon;
33//! use imxrt_ral as ral;
34//!
35//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
36//!
37//! // Init temperature monitor with 8Hz measure freq
38//! // 0xffff = 2 Sec. Read more at `measure_freq()`
39//! let mut temp_mon = TempMon::with_measure_freq(inst, 0x1000);
40//! let _ = temp_mon.start();
41//!
42//! let mut last_temp = 0_i32;
43//! loop {
44//!     // Get the last temperature read by the measure_freq
45//!     if let Ok(temp) = temp_mon.get_temp() {
46//!         if last_temp != temp {
47//!             // Temperature changed
48//!             last_temp = temp;
49//!         }
50//!         // Do something else
51//!     }
52//! }
53//! ```
54//!
55//! ## Example 3
56//!
57//! Low and high temperature Interrupt
58//!
59//! *NOTE*: TEMP_LOW_HIGH is triggered for `TempSensor low` and `TempSensor high`
60//!
61//! ```no_run
62//! use imxrt_hal::tempmon::TempMon;
63//! use imxrt_ral as ral;
64//!
65//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
66//!
67//! // Init temperature monitor with 8Hz measure freq
68//! // 0xffff = 2 Sec. Read more at `measure_freq()`
69//! let mut temp_mon = TempMon::with_measure_freq(inst, 0x1000);
70//!
71//! // Set low_alarm, high_alarm, and panic_alarm temperature
72//! temp_mon.set_alarm_values(-5_000, 65_000, 95_000);
73//!
74//! // Use values from registers if you like to compare it somewhere
75//! let (low_alarm, high_alarm, panic_alarm) = temp_mon.alarm_values();
76//!
77//! // Enables interrupts for low_high_alarm
78//! unsafe {
79//!     cortex_m::peripheral::NVIC::unmask(ral::interrupt::TEMP_LOW_HIGH);
80//! }
81//!
82//! // Start could fail if the module is not powered up
83//! if temp_mon.start().is_err() {
84//!     temp_mon.power_up();
85//!     let _ = temp_mon.start();
86//! }
87//!
88//! // #[cortex_m_rt::interrupt]
89//! fn TEMP_LOW_HIGH() {
90//!     // disable the interrupt to avoid endless triggers
91//!     cortex_m::peripheral::NVIC::mask(ral::interrupt::TEMP_LOW_HIGH);
92//!
93//!     // don't forget to enable it after the temperature is back to normal
94//! }
95//! ```
96
97use core::task::Poll;
98
99use crate::ral;
100
101/// Indicates that the temperature monitor is powered down.
102///
103/// If you receive this error, `power_up()` the temperature monitor first,
104/// and try again.
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub struct PowerDownError(());
108
109/// A Temperature Monitor (TEMPMON)
110///
111/// See the [module-level documentation](crate::tempmon) for important notes.
112///
113/// # Example
114///
115/// ```no_run
116/// use core::task::Poll;
117/// use imxrt_hal as hal;
118/// use imxrt_ral as ral;
119///
120/// let inst = unsafe { ral::tempmon::TEMPMON::instance() };
121/// let mut temp_mon = hal::tempmon::TempMon::new(inst);
122/// loop {
123///     if let Poll::Ready(Ok(_temperature)) = temp_mon.measure_temp() {
124///         // _temperature in mC (1°C = 1000°mC)
125///     }
126/// }
127/// ```
128pub struct TempMon {
129    base: ral::tempmon::TEMPMON,
130    /// Scaler
131    scaler: i32,
132    /// Hot_count
133    hot_count: i32,
134    /// Hot_temp * 1000
135    hot_temp: i32,
136}
137
138// We have to impl Debug by hand because the codegen'ed
139// ral::tempmon::TEMPMON doesn't impl Debug...
140impl core::fmt::Debug for TempMon {
141    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142        // Note: omit `base` which doesn't impl Debug or Display
143        fmt.debug_struct("TempMon")
144            .field("scaler", &format_args!("{}", self.scaler))
145            .field("hot_count", &format_args!("{}", self.hot_count))
146            .field("hot_temp", &format_args!("{}", self.hot_temp))
147            .finish()
148    }
149}
150
151#[cfg(feature = "defmt")]
152impl defmt::Format for TempMon {
153    fn format(&self, f: defmt::Formatter) {
154        defmt::write!(
155            f,
156            "TempMon {{ scaler: {}, hot_count: {}, hot_temp: {} }}",
157            self.scaler,
158            self.hot_count,
159            self.hot_temp,
160        )
161    }
162}
163
164impl TempMon {
165    /// Initialize and create the temperature monitor.
166    pub fn new(tempmon: ral::tempmon::TEMPMON) -> Self {
167        // Safety: This value is read-only and set by the manufacturer. imxrt-ral
168        // is constructed to always point at a valid OCOTP instance.
169        let calibration = unsafe { ral::read_reg!(ral::ocotp, OCOTP, ANA1) };
170
171        // The ral doesn't provide direct access to the values.
172        let n1_room_count = (calibration >> 20) as i32;
173        let t1_room_temp = 25_000_i32;
174        let n2_hot_count = ((calibration >> 8) & 0xFFF) as i32;
175        let t2_hot_temp = (calibration & 0xFF) as i32 * 1_000;
176
177        // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * ((HOT_TEMP - 25.0) / (ROOM_COUNT – HOT_COUNT))
178        let scaler = (t2_hot_temp - t1_room_temp) / (n1_room_count - n2_hot_count);
179        // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * scaler
180
181        let t = Self {
182            base: tempmon,
183            scaler,
184            hot_count: n2_hot_count,
185            hot_temp: t2_hot_temp,
186        };
187        t.power_up();
188        t
189    }
190    /// Initialize the temperature monitor.
191    ///
192    /// The `measure_freq` determines how many RTC clocks to wait before automatically repeating a temperature
193    /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period.
194    ///
195    /// Find more details [`set_measure_frequency`](TempMon::set_measure_frequency).
196    pub fn with_measure_freq(tempmon: ral::tempmon::TEMPMON, measure_freq: u16) -> Self {
197        let mut t = Self::new(tempmon);
198        t.set_measure_frequency(measure_freq);
199        t
200    }
201    /// Converts the temp_cnt into a human readable temperature [°mC] (1/1000 °C)
202    ///
203    /// param **temp_cnt**: measurement value from the tempmon module
204    ///
205    /// return: Temperature in °mC (1/1000°C)
206    fn convert(&self, temp_cnt: i32) -> i32 {
207        let n_meas = temp_cnt - self.hot_count;
208        self.hot_temp - n_meas * self.scaler
209    }
210
211    /// Decode the temp_value into measurable bytes
212    ///
213    /// param **temp_value_mc**: temperature value in °mC (1/1000°C)
214    ///
215    /// return: decoded temperature, compatible to the module internal measurements
216    fn decode(&self, temp_value_mc: i32) -> u32 {
217        let v = (temp_value_mc - self.hot_temp) / self.scaler;
218        (self.hot_count - v) as u32
219    }
220
221    /// Triggers a new measurement
222    ///
223    /// If you configured automatically repeating, this will trigger additional measurement.
224    /// Use get_temp instate to get the last read value
225    ///
226    /// The returning temperature in 1/1000 Celsius (°mC)
227    ///
228    /// Example: 25500°mC -> 25.5°C
229    pub fn measure_temp(&mut self) -> Poll<Result<i32, PowerDownError>> {
230        if !self.is_powered_up() {
231            Poll::Ready(Err(PowerDownError(())))
232        } else {
233            // If no measurement is active, trigger new measurement
234            let active = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, MEASURE_TEMP == START);
235            if !active {
236                ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START);
237            }
238
239            // If the measurement is not finished or not started
240            // i.MX Docs: This bit should be cleared by the sensor after the start of each measurement
241            if ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, FINISHED == INVALID) {
242                // measure_temp could be triggered again without any effect
243                Poll::Pending
244            } else {
245                // Clear MEASURE_TEMP to trigger a new measurement at the next call
246                ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START);
247
248                let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32;
249                Poll::Ready(Ok(self.convert(temp_cnt)))
250            }
251        }
252    }
253
254    /// Returns the last read value from the temperature sensor
255    ///
256    /// The returning temperature in 1/1000 Celsius (°mC)
257    ///
258    /// Example: 25500°mC -> 25.5°C
259    pub fn get_temp(&self) -> Result<i32, PowerDownError> {
260        if self.is_powered_up() {
261            let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32;
262            Ok(self.convert(temp_cnt))
263        } else {
264            Err(PowerDownError(()))
265        }
266    }
267
268    /// Starts the measurement process. If the measurement frequency is zero, this
269    /// results in a single conversion.
270    pub fn start(&mut self) -> Result<(), PowerDownError> {
271        if self.is_powered_up() {
272            ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START);
273            Ok(())
274        } else {
275            Err(PowerDownError(()))
276        }
277    }
278
279    /// Stops the measurement process. This only has an effect If the measurement
280    /// frequency is not zero.
281    pub fn stop(&self) {
282        ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START);
283    }
284
285    /// Returns the true if the tempmon module is powered up.
286    pub fn is_powered_up(&self) -> bool {
287        ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, POWER_DOWN == POWER_UP)
288    }
289
290    /// This powers down the temperature sensor.
291    pub fn power_down(&self) {
292        ral::write_reg!(
293            ral::tempmon,
294            self.base,
295            TEMPSENSE0_SET,
296            POWER_DOWN: POWER_DOWN
297        );
298    }
299
300    /// This powers up the temperature sensor.
301    pub fn power_up(&self) {
302        ral::write_reg!(
303            ral::tempmon,
304            self.base,
305            TEMPSENSE0_CLR,
306            POWER_DOWN: POWER_DOWN
307        );
308    }
309
310    /// Set the temperature that will generate a low alarm, high alarm, and panic alarm interrupt
311    /// when the temperature exceeded this values.
312    ///
313    /// ## Note:
314    /// low_alarm_mc, high_alarm_mc, and panic_alarm_mc are in milli Celsius (1/1000 °C)
315    pub fn set_alarm_values(&mut self, low_alarm_mc: i32, high_alarm_mc: i32, panic_alarm_mc: i32) {
316        let low_alarm = self.decode(low_alarm_mc);
317        let high_alarm = self.decode(high_alarm_mc);
318        let panic_alarm = self.decode(panic_alarm_mc);
319        ral::modify_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE: high_alarm);
320        ral::write_reg!(
321            ral::tempmon,
322            self.base,
323            TEMPSENSE2,
324            LOW_ALARM_VALUE: low_alarm,
325            PANIC_ALARM_VALUE: panic_alarm
326        );
327    }
328
329    /// Queries the temperature that will generate a low alarm, high alarm, and panic alarm interrupt.
330    ///
331    /// Returns (low_alarm_temp, high_alarm_temp, panic_alarm_temp)
332    pub fn alarm_values(&self) -> (i32, i32, i32) {
333        let high_alarm = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE);
334        let (low_alarm, panic_alarm) = ral::read_reg!(
335            ral::tempmon,
336            self.base,
337            TEMPSENSE2,
338            LOW_ALARM_VALUE,
339            PANIC_ALARM_VALUE
340        );
341        (
342            self.convert(low_alarm as i32),
343            self.convert(high_alarm as i32),
344            self.convert(panic_alarm as i32),
345        )
346    }
347
348    /// This bits determines how many RTC clocks to wait before automatically repeating a temperature
349    /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period.
350    ///
351    /// | value  | note |
352    /// | ------ | ----------------------------------------------------- |
353    /// | 0x0000 | Defines a single measurement with no repeat.          |
354    /// | 0x0001 | Updates the temperature value at a RTC clock rate.    |
355    /// | 0x0002 | Updates the temperature value at a RTC/2 clock rate.  |
356    /// | ...    | ... |
357    /// | 0xFFFF | Determines a two second sample period with a 32.768KHz RTC clock. Exact timings depend on the accuracy of the RTC clock.|
358    ///
359    pub fn set_measure_frequency(&mut self, measure_freq: u16) {
360        ral::modify_reg!(
361            ral::tempmon,
362            self.base,
363            TEMPSENSE1,
364            MEASURE_FREQ: measure_freq as u32
365        );
366    }
367}