Skip to main content

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}