esp_hal/
tsens.rs

1//! # Temperature Sensor (tsens)
2//!
3//! ## Overview
4//!
5//! The Temperature Sensor peripheral is used to measure the internal
6//! temperature inside the chip. The voltage is internally converted via an ADC
7//! into a digital value, and has a measuring range of –40 °C to 125 °C.
8//! The temperature value depends on factors like microcontroller clock
9//! frequency or I/O load. Generally, the chip’s internal temperature is higher
10//! than the operating ambient temperature.
11//!
12//! It is recommended to wait a few hundred microseconds after turning it on
13//! before measuring, in order to allow the sensor to stabilize.
14//!
15//! ## Configuration
16//!
17//! The temperature sensor can be configured with different clock sources.
18//!
19//! ## Examples
20//!
21//! The following example will measure the internal chip temperature every
22//! second, and print it
23//!
24//! ```rust, no_run
25#![doc = crate::before_snippet!()]
26//! # use esp_hal::tsens::{TemperatureSensor, Config};
27//! # use esp_hal::delay::Delay;
28//!
29//! let temperature_sensor = TemperatureSensor::new(
30//!         peripherals.TSENS,
31//!         Config::default())?;
32//! let delay = Delay::new();
33//! delay.delay_micros(200);
34//! loop {
35//!   let temp = temperature_sensor.get_temperature();
36//!   println!("Temperature: {:.2}°C", temp.to_celcius());
37//!   delay.delay_millis(1_000);
38//! }
39//! # }
40//! ```
41//! 
42//! ## Implementation State
43//!
44//! - Temperature calibration range is not supported
45//! - Interrupts are not supported
46
47use crate::{
48    peripheral::{Peripheral, PeripheralRef},
49    peripherals::{APB_SARADC, TSENS},
50    system::GenericPeripheralGuard,
51};
52
53/// Clock source for the temperature sensor
54#[derive(Debug, Clone, Default, PartialEq, Eq, Copy, Hash)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56#[non_exhaustive]
57pub enum ClockSource {
58    /// Use RC_FAST clock source
59    RcFast,
60    /// Use XTAL clock source
61    #[default]
62    Xtal,
63}
64
65/// Temperature sensor configuration
66#[derive(Debug, Clone, Default, PartialEq, Eq, Copy, Hash, procmacros::BuilderLite)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68#[non_exhaustive]
69pub struct Config {
70    /// Clock source for the temperature sensor
71    clock_source: ClockSource,
72}
73
74/// Temperature sensor configuration error
75#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
76#[non_exhaustive]
77pub enum ConfigError {}
78
79/// Temperature value
80/// This struct stores the raw ADC value, and can be used to calculate the
81/// temperature in Celsius using the formula:
82/// `(raw_value * 0.4386) - (offset * 27.88) - 20.52`
83#[derive(Debug)]
84pub struct Temperature {
85    /// Raw ADC value
86    pub raw_value: u8,
87
88    /// Offset value - depends on the temperature range configured
89    pub offset: i8,
90}
91
92impl Temperature {
93    /// Create a new temperature value
94    #[inline]
95    pub fn new(raw_value: u8, offset: i8) -> Self {
96        Self { raw_value, offset }
97    }
98
99    /// Get the temperature in Celsius
100    #[inline]
101    pub fn to_celsius(&self) -> f32 {
102        (self.raw_value as f32) * 0.4386 - (self.offset as f32) * 27.88 - 20.52
103    }
104
105    /// Get the temperature in Fahrenheit
106    #[inline]
107    pub fn to_fahrenheit(&self) -> f32 {
108        let celsius = self.to_celsius();
109        (celsius * 1.8) + 32.0
110    }
111
112    /// Get the temperature in Kelvin
113    #[inline]
114    pub fn to_kelvin(&self) -> f32 {
115        let celsius = self.to_celsius();
116        celsius + 273.15
117    }
118}
119
120/// Temperature sensor driver
121#[derive(Debug)]
122pub struct TemperatureSensor<'d> {
123    _peripheral: PeripheralRef<'d, TSENS>,
124    _tsens_guard: GenericPeripheralGuard<{ crate::system::Peripheral::Tsens as u8 }>,
125    _abp_saradc_guard: GenericPeripheralGuard<{ crate::system::Peripheral::ApbSarAdc as u8 }>,
126}
127
128impl<'d> TemperatureSensor<'d> {
129    /// Create a new temperature sensor instance with configuration
130    /// The sensor will be automatically powered up
131    pub fn new(
132        peripheral: impl Peripheral<P = TSENS> + 'd,
133        config: Config,
134    ) -> Result<Self, ConfigError> {
135        crate::into_ref!(peripheral);
136        // NOTE: We need enable ApbSarAdc before enabling Tsens
137        let apb_saradc_guard = GenericPeripheralGuard::new();
138        let tsens_guard = GenericPeripheralGuard::new();
139
140        let mut tsens = Self {
141            _peripheral: peripheral,
142            _tsens_guard: tsens_guard,
143            _abp_saradc_guard: apb_saradc_guard,
144        };
145        tsens.apply_config(&config)?;
146
147        tsens.power_up();
148
149        Ok(tsens)
150    }
151
152    /// Power up the temperature sensor
153    pub fn power_up(&self) {
154        debug!("Power up");
155        APB_SARADC::regs()
156            .tsens_ctrl()
157            .modify(|_, w| w.pu().set_bit());
158    }
159
160    /// Power down the temperature sensor - useful if you want to save power
161    pub fn power_down(&self) {
162        APB_SARADC::regs()
163            .tsens_ctrl()
164            .modify(|_, w| w.pu().clear_bit());
165    }
166
167    /// Change the temperature sensor configuration
168    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
169        // Set clock source
170        APB_SARADC::regs().tsens_ctrl2().write(|w| {
171            w.clk_sel()
172                .bit(matches!(config.clock_source, ClockSource::Xtal))
173        });
174
175        Ok(())
176    }
177
178    /// Get the raw temperature value
179    #[inline]
180    pub fn get_temperature(&self) -> Temperature {
181        let raw_value = APB_SARADC::regs().tsens_ctrl().read().out().bits();
182
183        // TODO Address multiple temperature ranges and offsets
184        let offset = -1i8;
185
186        Temperature::new(raw_value, offset)
187    }
188}