Skip to content

Commit

Permalink
feat: add support for internal temperature sensor (tsens) for esp32c6…
Browse files Browse the repository at this point in the history
… 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
davoclavo authored Jan 7, 2025
1 parent f1276f7 commit 5a64d9b
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 0 deletions.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `AnyPin::steal(pin_number)` (#2854)
- `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840)
- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840)
- Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875)

### Changed

Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ unstable_module! {
pub mod touch;
#[cfg(trace0)]
pub mod trace;
#[cfg(tsens)]
pub mod tsens;
#[cfg(any(twai0, twai1))]
pub mod twai;
#[cfg(usb_device)]
Expand Down
1 change: 1 addition & 0 deletions esp-hal/src/soc/esp32c3/peripherals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ crate::peripherals! {
SW_INTERRUPT <= virtual,
TIMG0 <= TIMG0,
TIMG1 <= TIMG1,
TSENS <= virtual,
TWAI0 <= TWAI0,
UART0 <= UART0,
UART1 <= UART1,
Expand Down
1 change: 1 addition & 0 deletions esp-hal/src/soc/esp32c6/peripherals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ crate::peripherals! {
TIMG0 <= TIMG0,
TIMG1 <= TIMG1,
TRACE0 <= TRACE,
TSENS <= virtual,
TWAI0 <= TWAI0,
TWAI1 <= TWAI1,
UART0 <= UART0,
Expand Down
36 changes: 36 additions & 0 deletions esp-hal/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ pub enum Peripheral {
/// Systimer peripheral.
#[cfg(systimer)]
Systimer,
/// Temperature sensor peripheral.
#[cfg(tsens)]
Tsens,
}

impl Peripheral {
Expand Down Expand Up @@ -398,6 +401,10 @@ impl PeripheralClockControl {
Peripheral::Systimer => {
perip_clk_en0.modify(|_, w| w.systimer_clk_en().bit(enable));
}
#[cfg(tsens)]
Peripheral::Tsens => {
perip_clk_en1.modify(|_, w| w.tsens_clk_en().bit(enable));
}
}
}

Expand Down Expand Up @@ -610,6 +617,16 @@ impl PeripheralClockControl {
perip_rst_en0.modify(|_, w| w.systimer_rst().set_bit());
perip_rst_en0.modify(|_, w| w.systimer_rst().clear_bit());
}
#[cfg(all(tsens, esp32c6))]
Peripheral::Tsens => {
perip_rst_en0.modify(|_, w| w.tsens_rst().set_bit());
perip_rst_en0.modify(|_, w| w.tsens_rst().clear_bit());
}
#[cfg(all(tsens, esp32c3))]
Peripheral::Tsens => {
perip_rst_en1.modify(|_, w| w.tsens_rst().set_bit());
perip_rst_en1.modify(|_, w| w.tsens_rst().clear_bit());
}
});
}
}
Expand Down Expand Up @@ -778,6 +795,16 @@ impl PeripheralClockControl {
.systimer_conf()
.modify(|_, w| w.systimer_clk_en().bit(enable));
}
#[cfg(tsens)]
Peripheral::Tsens => {
system
.tsens_clk_conf()
.modify(|_, w| w.tsens_clk_en().bit(enable));

system
.tsens_clk_conf()
.modify(|_, w| w.tsens_clk_sel().bit(enable));
}
}
}

Expand Down Expand Up @@ -977,6 +1004,15 @@ impl PeripheralClockControl {
.systimer_conf()
.modify(|_, w| w.systimer_rst_en().clear_bit());
}
#[cfg(tsens)]
Peripheral::Tsens => {
system
.tsens_clk_conf()
.modify(|_, w| w.tsens_rst_en().set_bit());
system
.tsens_clk_conf()
.modify(|_, w| w.tsens_rst_en().clear_bit());
}
}
}
}
Expand Down
189 changes: 189 additions & 0 deletions esp-hal/src/tsens.rs
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)
}
}
1 change: 1 addition & 0 deletions esp-metadata/devices/esp32c3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ symbols = [
"phy",
"bt",
"wifi",
"tsens",

# ROM capabilities
"rom_crc_le",
Expand Down
1 change: 1 addition & 0 deletions esp-metadata/devices/esp32c6.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ symbols = [
"wifi6",
"ieee802154",
"lp_core",
"tsens",

# ROM capabilities
"rom_crc_le",
Expand Down
33 changes: 33 additions & 0 deletions qa-test/src/bin/temperature_sensor.rs
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);
}
}

0 comments on commit 5a64d9b

Please sign in to comment.