1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//! Temperature monitor.
//!
//! ## IMPORTANT NOTE:
//!
//! On 10xx MCUs, the temperature sensor uses and assumes that the bandgap
//! reference, 480MHz PLL and 32KHz RTC modules are properly programmed and fully
//! settled for correct operation.
//!
//! ## Example 1
//!
//! Manually triggered read
//!
//! ```no_run
//! use imxrt_hal as hal;
//! use imxrt_ral as ral;
//!
//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
//! let mut temp_mon = hal::tempmon::TempMon::new(inst);
//! loop {
//!     if let Ok(temperature) = nb::block!(temp_mon.measure_temp()) {
//!         // Temperature in mC (1°C = 1000°mC)
//!     }
//! }
//! ```
//!
//! ## Example 2
//!
//! Non-blocking reading
//!
//! ```no_run
//! use imxrt_hal::tempmon::TempMon;
//! use imxrt_ral as ral;
//!
//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
//!
//! // Init temperature monitor with 8Hz measure freq
//! // 0xffff = 2 Sec. Read more at `measure_freq()`
//! let mut temp_mon = TempMon::with_measure_freq(inst, 0x1000);
//! temp_mon.start();
//!
//! let mut last_temp = 0_i32;
//! loop {
//!     // Get the last temperature read by the measure_freq
//!     if let Ok(temp) = temp_mon.get_temp() {
//!         if last_temp != temp {
//!             // Temperature changed
//!             last_temp = temp;
//!         }
//!         // Do something else
//!     }
//! }
//! ```
//!
//! ## Example 3
//!
//! Low and high temperature Interrupt
//!
//! *NOTE*: TEMP_LOW_HIGH is triggered for `TempSensor low` and `TempSensor high`
//!
//! ```no_run
//! use imxrt_hal::tempmon::TempMon;
//! use imxrt_ral as ral;
//!
//! let inst = unsafe { ral::tempmon::TEMPMON::instance() };
//!
//! // Init temperature monitor with 8Hz measure freq
//! // 0xffff = 2 Sec. Read more at `measure_freq()`
//! let mut temp_mon = TempMon::with_measure_freq(inst, 0x1000);
//!
//! // Set low_alarm, high_alarm, and panic_alarm temperature
//! temp_mon.set_alarm_values(-5_000, 65_000, 95_000);
//!
//! // Use values from registers if you like to compare it somewhere
//! let (low_alarm, high_alarm, panic_alarm) = temp_mon.alarm_values();
//!
//! // Enables interrupts for low_high_alarm
//! unsafe {
//!     cortex_m::peripheral::NVIC::unmask(ral::interrupt::TEMP_LOW_HIGH);
//! }
//!
//! // Start could fail if the module is not powered up
//! if temp_mon.start().is_err() {
//!     temp_mon.power_up();
//!     temp_mon.start();
//! }
//!
//! // #[cortex_m_rt::interrupt]
//! fn TEMP_LOW_HIGH() {
//!     // disable the interrupt to avoid endless triggers
//!     cortex_m::peripheral::NVIC::mask(ral::interrupt::TEMP_LOW_HIGH);
//!
//!     // don't forget to enable it after the temperature is back to normal
//! }
//! ```

use crate::ral;

/// Indicates that the temperature monitor is powered down.
///
/// If you receive this error, `power_up()` the temperature monitor first,
/// and try again.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PowerDownError(());

/// A Temperature Monitor (TEMPMON)
///
/// See the [module-level documentation](crate::tempmon) for important notes.
///
/// # Example
///
/// ```no_run
/// use imxrt_hal as hal;
/// use imxrt_ral as ral;
///
/// let inst = unsafe { ral::tempmon::TEMPMON::instance() };
/// let mut temp_mon = hal::tempmon::TempMon::new(inst);
/// loop {
///     if let Ok(_temperature) = nb::block!(temp_mon.measure_temp()) {
///         // _temperature in mC (1°C = 1000°mC)
///     }
/// }
/// ```
pub struct TempMon {
    base: ral::tempmon::TEMPMON,
    /// Scaler
    scaler: i32,
    /// Hot_count
    hot_count: i32,
    /// Hot_temp * 1000
    hot_temp: i32,
}

impl TempMon {
    /// Initialize and create the temperature monitor.
    pub fn new(tempmon: ral::tempmon::TEMPMON) -> Self {
        // this operation is safe. This value is read-only and set by the manufacturer.
        let calibration = unsafe { ral::read_reg!(ral::ocotp, OCOTP, ANA1) };

        // The ral doesn't provide direct access to the values.
        let n1_room_count = (calibration >> 20) as i32;
        let t1_room_temp = 25_000_i32;
        let n2_hot_count = ((calibration >> 8) & 0xFFF) as i32;
        let t2_hot_temp = (calibration & 0xFF) as i32 * 1_000;

        // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * ((HOT_TEMP - 25.0) / (ROOM_COUNT – HOT_COUNT))
        let scaler = (t2_hot_temp - t1_room_temp) / (n1_room_count - n2_hot_count);
        // Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * scaler

        let t = Self {
            base: tempmon,
            scaler,
            hot_count: n2_hot_count,
            hot_temp: t2_hot_temp,
        };
        t.power_up();
        t
    }
    /// Initialize the temperature monitor.
    ///
    /// The `measure_freq` determines how many RTC clocks to wait before automatically repeating a temperature
    /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period.
    ///
    /// Find more details [`set_measure_frequency`](TempMon::set_measure_frequency).
    pub fn with_measure_freq(tempmon: ral::tempmon::TEMPMON, measure_freq: u16) -> Self {
        let mut t = Self::new(tempmon);
        t.set_measure_frequency(measure_freq);
        t
    }
    /// Converts the temp_cnt into a human readable temperature [°mC] (1/1000 °C)
    ///
    /// param **temp_cnt**: measurement value from the tempmon module
    ///
    /// return: Temperature in °mC (1/1000°C)
    fn convert(&self, temp_cnt: i32) -> i32 {
        let n_meas = temp_cnt - self.hot_count;
        self.hot_temp - n_meas * self.scaler
    }

    /// Decode the temp_value into measurable bytes
    ///
    /// param **temp_value_mc**: temperature value in °mC (1/1000°C)
    ///
    /// return: decoded temperature, compatible to the module internal measurements
    fn decode(&self, temp_value_mc: i32) -> u32 {
        let v = (temp_value_mc - self.hot_temp) / self.scaler;
        (self.hot_count - v) as u32
    }

    /// Triggers a new measurement
    ///
    /// If you configured automatically repeating, this will trigger additional measurement.
    /// Use get_temp instate to get the last read value
    ///
    /// The returning temperature in 1/1000 Celsius (°mC)
    ///
    /// Example: 25500°mC -> 25.5°C
    pub fn measure_temp(&mut self) -> nb::Result<i32, PowerDownError> {
        if !self.is_powered_up() {
            Err(nb::Error::from(PowerDownError(())))
        } else {
            // If no measurement is active, trigger new measurement
            let active = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, MEASURE_TEMP == START);
            if !active {
                ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START);
            }

            // If the measurement is not finished or not started
            // i.MX Docs: This bit should be cleared by the sensor after the start of each measurement
            if ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, FINISHED == INVALID) {
                // measure_temp could be triggered again without any effect
                Err(nb::Error::WouldBlock)
            } else {
                // Clear MEASURE_TEMP to trigger a new measurement at the next call
                ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START);

                let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32;
                Ok(self.convert(temp_cnt))
            }
        }
    }

    /// Returns the last read value from the temperature sensor
    ///
    /// The returning temperature in 1/1000 Celsius (°mC)
    ///
    /// Example: 25500°mC -> 25.5°C
    pub fn get_temp(&self) -> nb::Result<i32, PowerDownError> {
        if self.is_powered_up() {
            let temp_cnt = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, TEMP_CNT) as i32;
            Ok(self.convert(temp_cnt))
        } else {
            Err(nb::Error::from(PowerDownError(())))
        }
    }

    /// Starts the measurement process. If the measurement frequency is zero, this
    /// results in a single conversion.
    pub fn start(&mut self) -> nb::Result<(), PowerDownError> {
        if self.is_powered_up() {
            ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_SET, MEASURE_TEMP: START);
            Ok(())
        } else {
            Err(nb::Error::from(PowerDownError(())))
        }
    }

    /// Stops the measurement process. This only has an effect If the measurement
    /// frequency is not zero.
    pub fn stop(&self) {
        ral::write_reg!(ral::tempmon, self.base, TEMPSENSE0_CLR, MEASURE_TEMP: START);
    }

    /// Returns the true if the tempmon module is powered up.
    pub fn is_powered_up(&self) -> bool {
        ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, POWER_DOWN == POWER_UP)
    }

    /// This powers down the temperature sensor.
    pub fn power_down(&self) {
        ral::write_reg!(
            ral::tempmon,
            self.base,
            TEMPSENSE0_SET,
            POWER_DOWN: POWER_DOWN
        );
    }

    /// This powers up the temperature sensor.
    pub fn power_up(&self) {
        ral::write_reg!(
            ral::tempmon,
            self.base,
            TEMPSENSE0_CLR,
            POWER_DOWN: POWER_DOWN
        );
    }

    /// Set the temperature that will generate a low alarm, high alarm, and panic alarm interrupt
    /// when the temperature exceeded this values.
    ///
    /// ## Note:
    /// low_alarm_mc, high_alarm_mc, and panic_alarm_mc are in milli Celsius (1/1000 °C)
    pub fn set_alarm_values(&mut self, low_alarm_mc: i32, high_alarm_mc: i32, panic_alarm_mc: i32) {
        let low_alarm = self.decode(low_alarm_mc);
        let high_alarm = self.decode(high_alarm_mc);
        let panic_alarm = self.decode(panic_alarm_mc);
        ral::modify_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE: high_alarm);
        ral::write_reg!(
            ral::tempmon,
            self.base,
            TEMPSENSE2,
            LOW_ALARM_VALUE: low_alarm,
            PANIC_ALARM_VALUE: panic_alarm
        );
    }

    /// Queries the temperature that will generate a low alarm, high alarm, and panic alarm interrupt.
    ///
    /// Returns (low_alarm_temp, high_alarm_temp, panic_alarm_temp)
    pub fn alarm_values(&self) -> (i32, i32, i32) {
        let high_alarm = ral::read_reg!(ral::tempmon, self.base, TEMPSENSE0, ALARM_VALUE);
        let (low_alarm, panic_alarm) = ral::read_reg!(
            ral::tempmon,
            self.base,
            TEMPSENSE2,
            LOW_ALARM_VALUE,
            PANIC_ALARM_VALUE
        );
        (
            self.convert(low_alarm as i32),
            self.convert(high_alarm as i32),
            self.convert(panic_alarm as i32),
        )
    }

    /// This bits determines how many RTC clocks to wait before automatically repeating a temperature
    /// measurement. The pause time before remeasuring is the field value multiplied by the RTC period.
    ///
    /// | value  | note |
    /// | ------ | ----------------------------------------------------- |
    /// | 0x0000 | Defines a single measurement with no repeat.          |
    /// | 0x0001 | Updates the temperature value at a RTC clock rate.    |
    /// | 0x0002 | Updates the temperature value at a RTC/2 clock rate.  |
    /// | ...    | ... |
    /// | 0xFFFF | Determines a two second sample period with a 32.768KHz RTC clock. Exact timings depend on the accuracy of the RTC clock.|
    ///
    pub fn set_measure_frequency(&mut self, measure_freq: u16) {
        ral::modify_reg!(
            ral::tempmon,
            self.base,
            TEMPSENSE1,
            MEASURE_FREQ: measure_freq as u32
        );
    }
}