-
Notifications
You must be signed in to change notification settings - Fork 239
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for internal temperature sensor (tsens) for esp32c6…
… and esp32c3 (#2875) * feat: add basic support for temperature sensor (tsens) for esp32c6 * feat: add basic support for temperature sensor (tsens) for esp32c3 * feat: add configurable clock source for temperature sensor * feat: add Temperature struct to avoid enforcing usage of floats - Also add misc derives to multiple structs - Add power_up / power_down methods to TemperatureSensor - Enable ApbSarAdc via PeripheralGuard * fix: move tsens module to unstable module list
- Loading branch information
Showing
9 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
//! # Temperature Sensor (tsens) | ||
//! | ||
//! ## Overview | ||
//! | ||
//! The Temperature Sensor peripheral is used to measure the internal | ||
//! temperature inside the chip. The voltage is internally converted via an ADC | ||
//! into a digital value, and has a measuring range of –40 °C to 125 °C. | ||
//! The temperature value depends on factors like microcontroller clock | ||
//! frequency or I/O load. Generally, the chip’s internal temperature is higher | ||
//! than the operating ambient temperature. | ||
//! | ||
//! It is recommended to wait a few hundred microseconds after turning it on | ||
//! before measuring, in order to allow the sensor to stabilize. | ||
//! | ||
//! ## Configuration | ||
//! | ||
//! The temperature sensor can be configured with different clock sources. | ||
//! | ||
//! ## Examples | ||
//! | ||
//! The following example will measure the internal chip temperature every | ||
//! second, and print it | ||
//! | ||
//! ```rust, no_run | ||
#![doc = crate::before_snippet!()] | ||
//! # use esp_hal::tsens::{TemperatureSensor, Config}; | ||
//! # use esp_hal::delay::Delay; | ||
//! | ||
//! let temperature_sensor = TemperatureSensor::new( | ||
//! peripherals.TSENS, | ||
//! Config::default() | ||
//! ).unwrap(); | ||
//! let delay = Delay::new(); | ||
//! delay.delay_micros(200); | ||
//! loop { | ||
//! let temp = temperature_sensor.get_temperature(); | ||
//! println!("Temperature: {:.2}°C", temp.to_celcius()); | ||
//! delay.delay_millis(1_000); | ||
//! } | ||
//! # } | ||
//! ``` | ||
//! | ||
//! ## Implementation State | ||
//! | ||
//! - Temperature calibration range is not supported | ||
//! - Interrupts are not supported | ||
use crate::{ | ||
peripheral::{Peripheral, PeripheralRef}, | ||
peripherals::TSENS, | ||
system::GenericPeripheralGuard, | ||
}; | ||
|
||
/// Clock source for the temperature sensor | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Copy, Hash)] | ||
#[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
pub enum ClockSource { | ||
/// Use RC_FAST clock source | ||
RcFast, | ||
/// Use XTAL clock source | ||
#[default] | ||
Xtal, | ||
} | ||
|
||
/// Temperature sensor configuration | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Copy, Hash, procmacros::BuilderLite)] | ||
#[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
#[non_exhaustive] | ||
pub struct Config { | ||
clock_source: ClockSource, | ||
} | ||
|
||
/// Temperature sensor configuration error | ||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] | ||
#[non_exhaustive] | ||
pub enum ConfigError {} | ||
|
||
/// Temperature value | ||
/// This struct stores the raw ADC value, and can be used to calculate the | ||
/// temperature in Celsius using the formula: | ||
/// `(raw_value * 0.4386) - (offset * 27.88) - 20.52` | ||
#[derive(Debug)] | ||
pub struct Temperature { | ||
/// Raw ADC value | ||
pub raw_value: u8, | ||
|
||
/// Offset value - depends on the temperature range configured | ||
pub offset: i8, | ||
} | ||
|
||
impl Temperature { | ||
/// Create a new temperature value | ||
#[inline] | ||
pub fn new(raw_value: u8, offset: i8) -> Self { | ||
Self { raw_value, offset } | ||
} | ||
|
||
/// Get the temperature in Celsius | ||
#[inline] | ||
pub fn to_celsius(&self) -> f32 { | ||
(self.raw_value as f32) * 0.4386 - (self.offset as f32) * 27.88 - 20.52 | ||
} | ||
|
||
/// Get the temperature in Fahrenheit | ||
#[inline] | ||
pub fn to_fahrenheit(&self) -> f32 { | ||
let celsius = self.to_celsius(); | ||
(celsius * 1.8) + 32.0 | ||
} | ||
|
||
/// Get the temperature in Kelvin | ||
#[inline] | ||
pub fn to_kelvin(&self) -> f32 { | ||
let celsius = self.to_celsius(); | ||
celsius + 273.15 | ||
} | ||
} | ||
|
||
/// Temperature sensor driver | ||
#[derive(Debug)] | ||
pub struct TemperatureSensor<'d> { | ||
_peripheral: PeripheralRef<'d, TSENS>, | ||
_tsens_guard: GenericPeripheralGuard<{ crate::system::Peripheral::Tsens as u8 }>, | ||
_abp_saradc_guard: GenericPeripheralGuard<{ crate::system::Peripheral::ApbSarAdc as u8 }>, | ||
} | ||
|
||
impl<'d> TemperatureSensor<'d> { | ||
/// Create a new temperature sensor instance with configuration | ||
/// The sensor will be automatically powered up | ||
pub fn new( | ||
peripheral: impl Peripheral<P = TSENS> + 'd, | ||
config: Config, | ||
) -> Result<Self, ConfigError> { | ||
crate::into_ref!(peripheral); | ||
// NOTE: We need enable ApbSarAdc before enabling Tsens | ||
let apb_saradc_guard = GenericPeripheralGuard::new(); | ||
let tsens_guard = GenericPeripheralGuard::new(); | ||
|
||
let mut tsens = Self { | ||
_peripheral: peripheral, | ||
_tsens_guard: tsens_guard, | ||
_abp_saradc_guard: apb_saradc_guard, | ||
}; | ||
tsens.apply_config(&config)?; | ||
|
||
tsens.power_up(); | ||
|
||
Ok(tsens) | ||
} | ||
|
||
/// Power up the temperature sensor | ||
pub fn power_up(&self) { | ||
debug!("Power up"); | ||
let abp_saradc = unsafe { &*crate::peripherals::APB_SARADC::PTR }; | ||
abp_saradc.tsens_ctrl().modify(|_, w| w.pu().set_bit()); | ||
} | ||
|
||
/// Power down the temperature sensor - useful if you want to save power | ||
pub fn power_down(&self) { | ||
let abp_saradc = unsafe { &*crate::peripherals::APB_SARADC::PTR }; | ||
abp_saradc.tsens_ctrl().modify(|_, w| w.pu().clear_bit()); | ||
} | ||
|
||
/// Change the temperature sensor configuration | ||
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::PTR }; | ||
|
||
// Set clock source | ||
apb_saradc.tsens_ctrl2().write(|w| { | ||
w.clk_sel() | ||
.bit(matches!(config.clock_source, ClockSource::Xtal)) | ||
}); | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Get the raw temperature value | ||
#[inline] | ||
pub fn get_temperature(&self) -> Temperature { | ||
let abp_saradc = unsafe { &*crate::peripherals::APB_SARADC::PTR }; | ||
|
||
let raw_value = abp_saradc.tsens_ctrl().read().out().bits(); | ||
|
||
// TODO Address multiple temperature ranges and offsets | ||
let offset = -1i8; | ||
|
||
Temperature::new(raw_value, offset) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ symbols = [ | |
"phy", | ||
"bt", | ||
"wifi", | ||
"tsens", | ||
|
||
# ROM capabilities | ||
"rom_crc_le", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,6 +83,7 @@ symbols = [ | |
"wifi6", | ||
"ieee802154", | ||
"lp_core", | ||
"tsens", | ||
|
||
# ROM capabilities | ||
"rom_crc_le", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//! This example uses the internal temperature sensor to measure the chip's | ||
//! temperature | ||
//% CHIPS: esp32c6 esp32c3 | ||
|
||
#![no_std] | ||
#![no_main] | ||
|
||
use esp_backtrace as _; | ||
use esp_hal::{ | ||
delay::Delay, | ||
entry, | ||
tsens::{Config, TemperatureSensor}, | ||
}; | ||
use esp_println::println; | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
esp_println::logger::init_logger_from_env(); | ||
let peripherals = esp_hal::init(esp_hal::Config::default()); | ||
|
||
let temperature_sensor = TemperatureSensor::new(peripherals.TSENS, Config::default()).unwrap(); | ||
let delay = Delay::new(); | ||
|
||
// Wait for the sensor to stabilize | ||
delay.delay_micros(200); | ||
|
||
loop { | ||
let temp = temperature_sensor.get_temperature(); | ||
println!("Temperature: {:.2}°C", temp.to_celsius()); | ||
delay.delay_millis(1_000); | ||
} | ||
} |