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}