hal_mik32/tsens.rs
1//! Temperature sensor (TSENS) HAL stub
2
3/// Конфигурация встроенного термодатчика
4///
5use mik32_pac::Tsens;
6
7use crate::rcc::{Clocks, HSI32M_FREQ, LSI32K_FREQ, OSC32K_FREQ, OSC32M_FREQ};
8
9const TSENS_OPTIMAL_FREQUENCY: u32 = 40000;
10///! Рекомендуемая частота работы термосенсора.
11
12/// Источник частоты встроенного датчика температуры
13///
14/// # Variants
15///
16/// - `SycClock = 0x0` - TODO:
17/// - `HLCL = 0x1` - TODO:
18/// - `OSC32M = 0x2` - внешний осциллятор 32МГц
19/// - `HSI32M = 0x3` - внутренний осциллятор 32МГц
20/// - `OSC32K = 0x4` - внешний осциллятор 32кГц
21/// - `LSI32K = 0x5` - внутренний осциллятор 32кГц
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23#[repr(u8)]
24pub enum ClockSource {
25 SycClock = 0x0,
26 HLCL = 0x1,
27 OSC32M = 0x2,
28 HSI32M = 0x3,
29 OSC32K = 0x4,
30 LSI32K = 0x5,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct Config {
35 pub source: ClockSource,
36 pub frequency: u32,
37}
38
39impl Config {
40 pub const fn default() -> Self {
41 Self {
42 source: ClockSource::SycClock,
43 frequency: TSENS_OPTIMAL_FREQUENCY,
44 }
45 }
46
47 /// Выбор источника тактирования термодатчика
48 ///
49 /// # Arguments
50 ///
51 /// - `source` (`ClockSource`) - источник
52 ///
53 /// # Returns
54 ///
55 /// - `Self` - объект конфигурации
56 ///
57 /// # Examples
58 ///
59 /// ```
60 /// let _ = clock_from_source(ClockSource::OSC32K);
61 /// ```
62 pub fn clock_from_source(mut self, source: ClockSource) -> Self {
63 self.source = source;
64 self
65 }
66
67 /// Установка частоты встроенного термодатчика
68 ///
69 /// # Arguments
70 ///
71 /// - `frequency` (`u32`) - частота
72 ///
73 /// # Returns
74 ///
75 /// - `Self` - объект конфигурации
76 ///
77 /// # Examples
78 ///
79 /// ```
80 /// let _ = with_frequency(40_000u32);
81 /// ```
82 pub fn with_frequency(mut self, frequency: u32) -> Self {
83 self.frequency = frequency;
84 self
85 }
86}
87
88/// Ошибки TSENS
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum Error {
91 /// Таймаут ожидания окончания преобразования
92 Timeout,
93 /// Порог (threshold) выходит за допустимый диапазон
94 ThresholdOutOfRange,
95 /// Частота, установленная для TSENS, выходит за допустимый диапазон
96 FrequencyOutOfRange,
97 /// Вычисленный делитель ниже нужного или 0
98 DividerUnderflow,
99 /// Вычисленный делитель выше максимального значения (1024)
100 DividerOverflow,
101 /// Другие ошибки аппаратного уровня
102 Hardware,
103}
104
105/// Встроенный термодатчи
106///
107/// # Fields
108///
109/// - `dp` (`Tsens`) - объект переферии из PAC
110/// - `config` (`Config`) - конфигурация встроенного термодатчика
111///
112/// # Examples
113///
114/// ```
115/// use crate::tsens::(TSENS, config::{Config, ClockSource});
116/// use mik32_pac::Peripherals;
117///
118/// let p = Peripherals::take().unwrap();
119/// let t_sensor = TSENS {
120/// dp: p.tsens,
121/// config: Config {
122/// source: ClockSource::OSC32K,
123/// frequency: 3000,
124/// },
125/// };
126///
127/// t_sensor.start_continuous();
128/// let temperature: u32 = t_sensor.get_temperature();
129/// ```
130pub struct TSENS {
131 dp: Tsens,
132 config: Config,
133}
134
135impl TSENS {
136 /// Конструктор объекта встренного термодатчика
137 ///
138 /// # Arguments
139 ///
140 /// - `dp` (`Tsens`) - объект переферии из PAC
141 /// - `clocks` (`&Clocks`) - объект тактирования RCC
142 /// - `config` (`Config`) - конфигурация термодатчика
143 ///
144 /// # Returns
145 ///
146 /// - `Result<Self, Error>` - построенный объект термодатчика или ошибка
147 ///
148 /// # Errors
149 ///
150 /// - делитель не находится не в допустимом промежутке: [1, 1023]
151 pub fn new(dp: Tsens, clocks: &Clocks, config: Config) -> Result<Self, Error> {
152 let result = Self {
153 dp: dp,
154 config: config,
155 };
156
157 // Подбор делителя для заданной частоты термосенсора
158 let real_clock = Self::get_real_clocks(&result.config, clocks);
159 let divider = Self::calc_divider(real_clock, &result.config)?;
160
161 // Установка источника тактирования термосенсора
162 result
163 .dp
164 .tsens_cfg()
165 .modify(|_, w| match result.config.source {
166 ClockSource::SycClock => w.clk_mux().sys_clk(),
167 ClockSource::HLCL => w.clk_mux().hclk(),
168 ClockSource::OSC32M => w.clk_mux().osc32m(),
169 ClockSource::HSI32M => w.clk_mux().hsi32m(),
170 ClockSource::OSC32K => w.clk_mux().osc32k(),
171 ClockSource::LSI32K => w.clk_mux().lsi32k(),
172 });
173
174 result.dp.tsens_cfg().modify(|_, w| unsafe {
175 w.nrst()
176 .set_bit() // уберём сброс
177 .npd_clk()
178 .set_bit() // включим тактирование
179 .npd()
180 .set_bit() // включим датчик
181 .div()
182 .bits(divider as u16)
183 });
184 Ok(result)
185 }
186
187 /// Выполняет одиночное измерение температуры и возвращает результат в градусах Цельсия
188 ///
189 /// Функция выводит TSENS из сброса, запускает одиночное преобразование и
190 /// ожидает установку флага `EOC` (End Of Conversion) в регистре `TSENS_VALUE`.
191 ///
192 /// # Параметры
193 /// - `timeout`: Максимальное число итераций ожидания `EOC`.
194 /// Если `None`, используется значение по умолчанию `100_000`.
195 ///
196 /// # Возвращает
197 /// - `Ok(temp_c)`: Измеренная температура в градусах Цельсия.
198 /// - `Err(Error::Timeout)`: Если `EOC` не установился до исчерпания `timeout`.
199 ///
200 /// # Примечания
201 /// - Таймаут задаётся в “тиках” цикла ожидания (не в микросекундах).
202 /// - Значение температуры вычисляется через `value_to_celsius()` из сырого
203 /// значения регистра `TSENS_VALUE`.
204 pub fn single_measurement(&mut self, timeout: Option<u32>) -> Result<u32, Error> {
205 let mut timeout_counter: u32 = timeout.unwrap_or(100_000);
206
207 self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
208 self.dp.tsens_single().write(|w| w.single().set_bit());
209
210 while !self.dp.tsens_value().read().eoc().bit_is_set() {
211 timeout_counter = timeout_counter.checked_sub(1).ok_or(Error::Timeout)?;
212 }
213
214 Ok(value_to_celsius(
215 self.dp.tsens_value().read().bits() & (0x3FF << 0),
216 ))
217 }
218
219 /// Запускает непрерываное измерение температуры с прерываниями
220 ///
221 /// # Returns
222 ///
223 /// - `Self` - экземпляр TSENS с запущенным непрерывным режимом измерения
224 pub fn start_continuous_interrupt(&mut self) {
225 self.dp.tsens_clear_irq().write(|w| unsafe {
226 w.bits(0b111) // Очистим все флаги прерываний
227 });
228
229 self.dp.tsens_irq().write(|w| unsafe {
230 w.bits(0b111) // Включим все маски прерываний
231 });
232
233 self.dp.tsens_cfg().modify(|_, w| {
234 w.nrst().clear_bit() // уберём сброс
235 });
236
237 self.dp.tsens_continuous().write(|w| {
238 w.continuous().set_bit() // Запустим непрерывный режим
239 });
240 }
241
242 /// Запускает однократное измерение с прерываниями (один замер, затем остановка)
243 ///
244 /// # Returns
245 ///
246 /// - `Self` - экземпляр TSENS с запущенным однократным режимом измерения
247 pub fn start_single_interrupt(&mut self) {
248 self.dp.tsens_clear_irq().write(|w| unsafe {
249 w.bits(0b111) // Очистим все флаги прерываний
250 });
251
252 self.dp.tsens_irq().write(|w| unsafe {
253 w.bits(0b111) // Включим все маски прерываний
254 });
255
256 self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
257
258 self.dp.tsens_single().write(|w| {
259 w.single().set_bit() // Запустим однократный режим
260 });
261 }
262
263 /// Останавливает прерывания
264 ///
265 /// # Arguments
266 ///
267 /// # Returns
268 ///
269 /// - `Self` - экземпляр TSENS с остановленными измерениями и отключенными прерываниями
270 pub fn stop_interrupt(self) -> Self {
271 self.dp.tsens_cfg().modify(|_, w| w.nrst().clear_bit());
272
273 self.dp.tsens_irq().write(|w| unsafe {
274 w.bits(0b000) // Отключим все маски прерываний
275 });
276
277 self.dp.tsens_clear_irq().write(|w| unsafe {
278 w.bits(0b111) // Очистим все флаги прерываний
279 });
280
281 self
282 }
283
284 /// Установка верхнего порога температуры
285 ///
286 /// # Arguments
287 ///
288 /// - `value` (`u32`) - температура в цельсиях
289 ///
290 /// # Returns
291 ///
292 /// - `Result<Self, Error>` - результат операции`
293 pub fn on_upper_threshold(&mut self, value: u32) -> Result<(), Error> {
294 let raw_value = celsius_to_value(value) as u32;
295
296 if (raw_value > 603u32) || (raw_value < 255u32) {
297 return Err(Error::ThresholdOutOfRange);
298 }
299
300 self.dp.tsens_treshold().modify(|_, w| unsafe {
301 w.treshold_hi().bits(raw_value as u16) // Transform value to
302 });
303 Ok(())
304 }
305
306 /// Установка нижнего порога температуры
307 ///
308 /// # Arguments
309 ///
310 /// - `value` (`u32`) - температура в цельсиях
311 ///
312 /// # Returns
313 ///
314 /// - `Result<Self, Error>` - результат операции`
315 pub fn on_lower_threshold(self, value: u32) -> Result<(), Error> {
316 let raw_value = celsius_to_value(value) as u32;
317
318 if (raw_value > 603u32) || (raw_value < 255u32) {
319 return Err(Error::ThresholdOutOfRange);
320 }
321
322 self.dp
323 .tsens_treshold()
324 .modify(|_, w| unsafe { w.treshold_low().bits(raw_value as u16) });
325 Ok(())
326 }
327
328 /// Запускает режим непрерывного измерения температуры
329 pub fn start_continuous(&mut self) {
330 self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
331 self.dp.tsens_continuous().write(|w| unsafe { w.bits(1) });
332 }
333
334 /// Текущая температура в цельсиях
335 ///
336 /// Функция используется для получения значения температуры
337 /// в непрерывном режиме измерения (Continuous)
338 ///
339 /// # Returns
340 ///
341 /// - `u32` - температура в цельсиях
342 pub fn get_temperature(&self) -> u32 {
343 value_to_celsius(self.dp.tsens_value().read().value().bits() as u32)
344 }
345
346 /// Вычисляет делитель для достижения оптимальной частоты работы TSENS (40 кГц)
347 ///
348 /// # Arguments
349 ///
350 /// - `value` (`u32`) - желаемая частота работы TSENS в герцах
351 /// - `clocks` (`&Clocks`) - структура с текущими частотами тактирования
352 ///
353 /// # Returns
354 ///
355 /// - `Result<u32, Error>` - вычисленный делитель или ошибка, если частота вне допустимого диапазона
356 fn calc_divider(real_clock: u32, config: &Config) -> Result<u32, Error> {
357 if config.frequency == 0 || config.frequency > 100_000 {
358 return Err(Error::FrequencyOutOfRange);
359 }
360
361 let mut divider = (real_clock / config.frequency) >> 1;
362 if divider == 0 {
363 return Err(Error::DividerUnderflow);
364 }
365
366 let mut pre_result = real_clock / (divider << 1);
367 while (pre_result > 100_000) && (divider <= 0x400) {
368 divider += 1;
369 if divider > 0x400 {
370 return Err(Error::DividerOverflow);
371 }
372 pre_result = real_clock / (divider << 1);
373 }
374
375 divider = divider - 1;
376
377 if divider >= 1024 {
378 return Err(Error::DividerOverflow);
379 }
380
381 Ok(divider)
382 }
383
384 /// Получает реальную частоту тактирования для TSENS на основе выбранного источника и текущих настроек тактирования
385 ///
386 /// # Arguments
387 ///
388 /// - `config` (`&Config`) - конфигурация TSENS, содержащая выбранный источник тактирования
389 /// - `clocks` (`&Clocks`) - структура с текущими частотами тактирования
390 ///
391 /// # Returns
392 ///
393 /// - `u32` - реальная частота тактирования для TSENS в герцах
394 fn get_real_clocks(config: &Config, clocks: &Clocks) -> u32 {
395 match config.source {
396 ClockSource::SycClock => clocks.ahbclk().0,
397 ClockSource::HLCL => clocks.ahbclk().0 / (clocks.ahb_div_clk() + 1),
398 ClockSource::OSC32M => OSC32M_FREQ.0,
399 ClockSource::HSI32M => HSI32M_FREQ.0,
400 ClockSource::OSC32K => OSC32K_FREQ.0,
401 ClockSource::LSI32K => LSI32K_FREQ.0,
402 }
403 }
404}
405
406/// Конвертирует внутренее значение датчика температуры в цельсиях
407///
408/// # Arguments
409///
410/// - `value` (`u32`) - значение датчика температуры
411///
412/// # Returns
413///
414/// - `u32` - температура в цельсиях
415#[inline(always)]
416fn value_to_celsius(value: u32) -> u32 {
417 // return (640660 * value) / (40960 + 93 * value) * 10 - 27315;
418 return ((640_660u32 * value) / (40_960u32 + 93u32 * value) * 10 - 27_315) as u32;
419}
420
421/// Конвертирует температуру в цельсиях во внутренее значение датчика TSENS
422///
423/// # Arguments
424///
425/// - `value` (`u32`) - температура в цельсиях
426///
427/// # Returns
428///
429/// - `u32` - внутренее значение датчика TSENS
430#[inline(always)]
431fn celsius_to_value(value: u32) -> u32 {
432 return 40960 * 100 / (((6406600 - 93 * (value * 100 + 27315)) * 100) / (value * 100 + 27315));
433}