use mik32_pac::Tsens;
use crate::rcc::{Clocks, HSI32M_FREQ, LSI32K_FREQ, OSC32K_FREQ, OSC32M_FREQ};
const TSENS_OPTIMAL_FREQUENCY: u32 = 40000;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum ClockSource {
SycClock = 0x0,
HLCL = 0x1,
OSC32M = 0x2,
HSI32M = 0x3,
OSC32K = 0x4,
LSI32K = 0x5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub source: ClockSource,
pub frequency: u32,
}
impl Config {
pub const fn default() -> Self {
Self {
source: ClockSource::SycClock,
frequency: TSENS_OPTIMAL_FREQUENCY,
}
}
pub fn clock_from_source(mut self, source: ClockSource) -> Self {
self.source = source;
self
}
pub fn with_frequency(mut self, frequency: u32) -> Self {
self.frequency = frequency;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
Timeout,
ThresholdOutOfRange,
FrequencyOutOfRange,
DividerUnderflow,
DividerOverflow,
Hardware,
}
pub struct TSENS {
dp: Tsens,
config: Config,
}
impl TSENS {
pub fn new(dp: Tsens, clocks: &Clocks, config: Config) -> Result<Self, Error> {
let result = Self {
dp: dp,
config: config,
};
let real_clock = Self::get_real_clocks(&result.config, clocks);
let divider = Self::calc_divider(real_clock, &result.config)?;
result
.dp
.tsens_cfg()
.modify(|_, w| match result.config.source {
ClockSource::SycClock => w.clk_mux().sys_clk(),
ClockSource::HLCL => w.clk_mux().hclk(),
ClockSource::OSC32M => w.clk_mux().osc32m(),
ClockSource::HSI32M => w.clk_mux().hsi32m(),
ClockSource::OSC32K => w.clk_mux().osc32k(),
ClockSource::LSI32K => w.clk_mux().lsi32k(),
});
result.dp.tsens_cfg().modify(|_, w| unsafe {
w.nrst()
.set_bit() .npd_clk()
.set_bit() .npd()
.set_bit() .div()
.bits(divider as u16)
});
Ok(result)
}
pub fn single_measurement(&mut self, timeout: Option<u32>) -> Result<u32, Error> {
let mut timeout_counter: u32 = timeout.unwrap_or(100_000);
self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
self.dp.tsens_single().write(|w| w.single().set_bit());
while !self.dp.tsens_value().read().eoc().bit_is_set() {
timeout_counter = timeout_counter.checked_sub(1).ok_or(Error::Timeout)?;
}
Ok(value_to_celsius(
self.dp.tsens_value().read().bits() & (0x3FF << 0),
))
}
pub fn start_continuous_interrupt(&mut self) {
self.dp.tsens_clear_irq().write(|w| unsafe {
w.bits(0b111) });
self.dp.tsens_irq().write(|w| unsafe {
w.bits(0b111) });
self.dp.tsens_cfg().modify(|_, w| {
w.nrst().clear_bit() });
self.dp.tsens_continuous().write(|w| {
w.continuous().set_bit() });
}
pub fn start_single_interrupt(&mut self) {
self.dp.tsens_clear_irq().write(|w| unsafe {
w.bits(0b111) });
self.dp.tsens_irq().write(|w| unsafe {
w.bits(0b111) });
self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
self.dp.tsens_single().write(|w| {
w.single().set_bit() });
}
pub fn stop_interrupt(self) -> Self {
self.dp.tsens_cfg().modify(|_, w| w.nrst().clear_bit());
self.dp.tsens_irq().write(|w| unsafe {
w.bits(0b000) });
self.dp.tsens_clear_irq().write(|w| unsafe {
w.bits(0b111) });
self
}
pub fn on_upper_threshold(&mut self, value: u32) -> Result<(), Error> {
let raw_value = celsius_to_value(value) as u32;
if (raw_value > 603u32) || (raw_value < 255u32) {
return Err(Error::ThresholdOutOfRange);
}
self.dp.tsens_treshold().modify(|_, w| unsafe {
w.treshold_hi().bits(raw_value as u16) });
Ok(())
}
pub fn on_lower_threshold(self, value: u32) -> Result<(), Error> {
let raw_value = celsius_to_value(value) as u32;
if (raw_value > 603u32) || (raw_value < 255u32) {
return Err(Error::ThresholdOutOfRange);
}
self.dp
.tsens_treshold()
.modify(|_, w| unsafe { w.treshold_low().bits(raw_value as u16) });
Ok(())
}
pub fn start_continuous(&mut self) {
self.dp.tsens_cfg().modify(|_, w| w.nrst().set_bit());
self.dp.tsens_continuous().write(|w| unsafe { w.bits(1) });
}
pub fn get_temperature(&self) -> u32 {
value_to_celsius(self.dp.tsens_value().read().value().bits() as u32)
}
fn calc_divider(real_clock: u32, config: &Config) -> Result<u32, Error> {
if config.frequency == 0 || config.frequency > 100_000 {
return Err(Error::FrequencyOutOfRange);
}
let mut divider = (real_clock / config.frequency) >> 1;
if divider == 0 {
return Err(Error::DividerUnderflow);
}
let mut pre_result = real_clock / (divider << 1);
while (pre_result > 100_000) && (divider <= 0x400) {
divider += 1;
if divider > 0x400 {
return Err(Error::DividerOverflow);
}
pre_result = real_clock / (divider << 1);
}
divider = divider - 1;
if divider >= 1024 {
return Err(Error::DividerOverflow);
}
Ok(divider)
}
fn get_real_clocks(config: &Config, clocks: &Clocks) -> u32 {
match config.source {
ClockSource::SycClock => clocks.ahbclk().0,
ClockSource::HLCL => clocks.ahbclk().0 / (clocks.ahb_div_clk() + 1),
ClockSource::OSC32M => OSC32M_FREQ.0,
ClockSource::HSI32M => HSI32M_FREQ.0,
ClockSource::OSC32K => OSC32K_FREQ.0,
ClockSource::LSI32K => LSI32K_FREQ.0,
}
}
}
#[inline(always)]
fn value_to_celsius(value: u32) -> u32 {
return ((640_660u32 * value) / (40_960u32 + 93u32 * value) * 10 - 27_315) as u32;
}
#[inline(always)]
fn celsius_to_value(value: u32) -> u32 {
return 40960 * 100 / (((6406600 - 93 * (value * 100 + 27315)) * 100) / (value * 100 + 27315));
}