Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
Loading