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
);
}
}