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
This commit is contained in:
parent
f1276f7d1b
commit
5a64d9ba8f
@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added `AnyPin::steal(pin_number)` (#2854)
|
- Added `AnyPin::steal(pin_number)` (#2854)
|
||||||
- `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840)
|
- `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)
|
- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840)
|
||||||
|
- Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@ -267,6 +267,8 @@ unstable_module! {
|
|||||||
pub mod touch;
|
pub mod touch;
|
||||||
#[cfg(trace0)]
|
#[cfg(trace0)]
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
|
#[cfg(tsens)]
|
||||||
|
pub mod tsens;
|
||||||
#[cfg(any(twai0, twai1))]
|
#[cfg(any(twai0, twai1))]
|
||||||
pub mod twai;
|
pub mod twai;
|
||||||
#[cfg(usb_device)]
|
#[cfg(usb_device)]
|
||||||
|
|||||||
@ -52,6 +52,7 @@ crate::peripherals! {
|
|||||||
SW_INTERRUPT <= virtual,
|
SW_INTERRUPT <= virtual,
|
||||||
TIMG0 <= TIMG0,
|
TIMG0 <= TIMG0,
|
||||||
TIMG1 <= TIMG1,
|
TIMG1 <= TIMG1,
|
||||||
|
TSENS <= virtual,
|
||||||
TWAI0 <= TWAI0,
|
TWAI0 <= TWAI0,
|
||||||
UART0 <= UART0,
|
UART0 <= UART0,
|
||||||
UART1 <= UART1,
|
UART1 <= UART1,
|
||||||
|
|||||||
@ -80,6 +80,7 @@ crate::peripherals! {
|
|||||||
TIMG0 <= TIMG0,
|
TIMG0 <= TIMG0,
|
||||||
TIMG1 <= TIMG1,
|
TIMG1 <= TIMG1,
|
||||||
TRACE0 <= TRACE,
|
TRACE0 <= TRACE,
|
||||||
|
TSENS <= virtual,
|
||||||
TWAI0 <= TWAI0,
|
TWAI0 <= TWAI0,
|
||||||
TWAI1 <= TWAI1,
|
TWAI1 <= TWAI1,
|
||||||
UART0 <= UART0,
|
UART0 <= UART0,
|
||||||
|
|||||||
@ -132,6 +132,9 @@ pub enum Peripheral {
|
|||||||
/// Systimer peripheral.
|
/// Systimer peripheral.
|
||||||
#[cfg(systimer)]
|
#[cfg(systimer)]
|
||||||
Systimer,
|
Systimer,
|
||||||
|
/// Temperature sensor peripheral.
|
||||||
|
#[cfg(tsens)]
|
||||||
|
Tsens,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Peripheral {
|
impl Peripheral {
|
||||||
@ -398,6 +401,10 @@ impl PeripheralClockControl {
|
|||||||
Peripheral::Systimer => {
|
Peripheral::Systimer => {
|
||||||
perip_clk_en0.modify(|_, w| w.systimer_clk_en().bit(enable));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,6 +617,16 @@ impl PeripheralClockControl {
|
|||||||
perip_rst_en0.modify(|_, w| w.systimer_rst().set_bit());
|
perip_rst_en0.modify(|_, w| w.systimer_rst().set_bit());
|
||||||
perip_rst_en0.modify(|_, w| w.systimer_rst().clear_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());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -778,6 +795,16 @@ impl PeripheralClockControl {
|
|||||||
.systimer_conf()
|
.systimer_conf()
|
||||||
.modify(|_, w| w.systimer_clk_en().bit(enable));
|
.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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,6 +1004,15 @@ impl PeripheralClockControl {
|
|||||||
.systimer_conf()
|
.systimer_conf()
|
||||||
.modify(|_, w| w.systimer_rst_en().clear_bit());
|
.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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
189
esp-hal/src/tsens.rs
Normal file
189
esp-hal/src/tsens.rs
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,6 +52,7 @@ symbols = [
|
|||||||
"phy",
|
"phy",
|
||||||
"bt",
|
"bt",
|
||||||
"wifi",
|
"wifi",
|
||||||
|
"tsens",
|
||||||
|
|
||||||
# ROM capabilities
|
# ROM capabilities
|
||||||
"rom_crc_le",
|
"rom_crc_le",
|
||||||
|
|||||||
@ -83,6 +83,7 @@ symbols = [
|
|||||||
"wifi6",
|
"wifi6",
|
||||||
"ieee802154",
|
"ieee802154",
|
||||||
"lp_core",
|
"lp_core",
|
||||||
|
"tsens",
|
||||||
|
|
||||||
# ROM capabilities
|
# ROM capabilities
|
||||||
"rom_crc_le",
|
"rom_crc_le",
|
||||||
|
|||||||
33
qa-test/src/bin/temperature_sensor.rs
Normal file
33
qa-test/src/bin/temperature_sensor.rs
Normal file
@ -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…
Reference in New Issue
Block a user