Skip to content

Commit acaa931

Browse files
committed
stm32: Implement reads of DTS peripheral
Only PCLK-driven operation is supported. Similarly, only software triggering is supported.
1 parent 2cc50ba commit acaa931

File tree

3 files changed

+306
-0
lines changed

3 files changed

+306
-0
lines changed

embassy-stm32/src/dts.rs

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
//! Digital Temperature Sensor (DTS)
2+
3+
use core::future::poll_fn;
4+
use core::sync::atomic::{compiler_fence, Ordering};
5+
use core::task::Poll;
6+
7+
use embassy_hal_internal::{into_ref, PeripheralRef};
8+
use embassy_sync::waitqueue::AtomicWaker;
9+
10+
use crate::interrupt::InterruptExt;
11+
use crate::peripherals::DTS;
12+
use crate::time::Hertz;
13+
use crate::{interrupt, pac, rcc, Peripheral};
14+
15+
#[allow(missing_docs)]
16+
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
17+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18+
pub enum SampleTime {
19+
ClockCycles1 = 1,
20+
ClockCycles2 = 2,
21+
ClockCycles3 = 3,
22+
ClockCycles4 = 4,
23+
ClockCycles5 = 5,
24+
ClockCycles6 = 6,
25+
ClockCycles7 = 7,
26+
ClockCycles8 = 8,
27+
ClockCycles9 = 9,
28+
ClockCycles10 = 10,
29+
ClockCycles11 = 11,
30+
ClockCycles12 = 12,
31+
ClockCycles13 = 13,
32+
ClockCycles14 = 14,
33+
ClockCycles15 = 15,
34+
}
35+
36+
/// The read-only factory calibration values used for converting a
37+
/// measurement to a temperature.
38+
#[derive(Clone, Debug)]
39+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40+
pub struct FactoryCalibration {
41+
/// The calibration temperature in degrees Celsius.
42+
pub t0: u8,
43+
/// The frequency at the calibration temperature.
44+
pub fmt0: Hertz,
45+
/// The ramp coefficient in Hertz per degree Celsius.
46+
pub ramp_coeff: u16,
47+
}
48+
49+
const MAX_DTS_CLK_FREQ: Hertz = Hertz::mhz(1);
50+
51+
/// Digital temperature sensor driver.
52+
pub struct Dts<'d> {
53+
_peri: PeripheralRef<'d, DTS>,
54+
sample_time: SampleTime,
55+
}
56+
57+
static WAKER: AtomicWaker = AtomicWaker::new();
58+
59+
impl<'d> Dts<'d> {
60+
/// Create a new temperature sensor driver.
61+
pub fn new(
62+
_peri: impl Peripheral<P = DTS> + 'd,
63+
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::DTS, InterruptHandler> + 'd,
64+
sample_time: SampleTime,
65+
) -> Self {
66+
into_ref!(_peri);
67+
rcc::enable_and_reset::<DTS>();
68+
69+
let prescaler = rcc::frequency::<DTS>() / MAX_DTS_CLK_FREQ;
70+
71+
if prescaler > 127 {
72+
panic!("DTS PCLK frequency must be less than 127 MHz.");
73+
}
74+
75+
Self::regs().cfgr1().modify(|w| {
76+
w.set_refclk_sel(false);
77+
w.set_hsref_clk_div(prescaler as u8);
78+
w.set_q_meas_opt(false);
79+
// Software trigger
80+
w.set_intrig_sel(0);
81+
w.set_smp_time(sample_time as u8);
82+
w.set_start(true);
83+
w.set_en(true);
84+
});
85+
86+
interrupt::DTS.unpend();
87+
unsafe { interrupt::DTS.enable() };
88+
89+
Self { _peri, sample_time }
90+
}
91+
92+
/// Set the DTS sample time.
93+
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
94+
Self::regs().cfgr1().modify(|w| w.set_start(false));
95+
96+
// Wait for the stop to be taken into account.
97+
while !Self::regs().sr().read().rdy() {}
98+
99+
Self::regs().cfgr1().modify(|w| {
100+
w.set_smp_time(sample_time as u8);
101+
w.set_start(true);
102+
});
103+
104+
self.sample_time = sample_time;
105+
}
106+
107+
/// Get the DTS sample time.
108+
pub fn sample_time(&self) -> SampleTime {
109+
self.sample_time
110+
}
111+
112+
/// Get the read-only factory calibration values used for converting a
113+
/// measurement to a temperature.
114+
pub fn factory_calibration() -> FactoryCalibration {
115+
let t0valr1 = Self::regs().t0valr1().read();
116+
let t0 = match t0valr1.t0() {
117+
0 => 30,
118+
1 => 130,
119+
_ => unimplemented!(),
120+
};
121+
let fmt0 = Hertz::hz(t0valr1.fmt0() as u32 * 100);
122+
123+
let ramp_coeff = Self::regs().rampvalr().read().ramp_coeff();
124+
125+
FactoryCalibration { t0, fmt0, ramp_coeff }
126+
}
127+
128+
/// Perform an asynchonous temperature measurement. The returned future can
129+
/// be awaited to obtain the measurement.
130+
///
131+
/// Measurements are continuously made, the future returned waits for the
132+
/// next measurement to complete.
133+
///
134+
/// # Example
135+
///
136+
/// ```no_run
137+
/// use embassy_stm32::{bind_interrupts, dts};
138+
/// use embassy_stm32::dts::Dts;
139+
///
140+
/// bind_interrupts!(struct Irqs {
141+
/// DTS => temp::InterruptHandler;
142+
/// });
143+
///
144+
/// # async {
145+
/// # let p: embassy_stm32::Peripherals = todo!();
146+
/// let mut dts = Dts::new(p.DTS, Irqs);
147+
/// let v: u16 = dts.read().await;
148+
/// # };
149+
/// ```
150+
pub async fn read(&mut self) -> u16 {
151+
let r = Self::regs();
152+
153+
r.itenr().modify(|w| w.set_iteen(true));
154+
155+
poll_fn(|cx| {
156+
WAKER.register(cx.waker());
157+
if r.itenr().read().iteen() {
158+
Poll::Pending
159+
} else {
160+
Poll::Ready(r.dr().read().mfreq())
161+
}
162+
})
163+
.await
164+
}
165+
166+
/// Returns the last measurement made, if any.
167+
///
168+
/// There is no guarantee that the measurement is recent or that a
169+
/// measurement has ever completed.
170+
pub fn read_immediate(&mut self) -> u16 {
171+
Self::regs().dr().read().mfreq()
172+
}
173+
174+
fn regs() -> pac::dts::Dts {
175+
pac::DTS
176+
}
177+
}
178+
179+
impl<'d> Drop for Dts<'d> {
180+
fn drop(&mut self) {
181+
Self::regs().cfgr1().modify(|w| w.set_en(false));
182+
rcc::disable::<DTS>();
183+
}
184+
}
185+
186+
/// Interrupt handler.
187+
pub struct InterruptHandler {
188+
_private: (),
189+
}
190+
191+
impl interrupt::typelevel::Handler<interrupt::typelevel::DTS> for InterruptHandler {
192+
unsafe fn on_interrupt() {
193+
let r = pac::DTS;
194+
let (sr, itenr) = (r.sr().read(), r.itenr().read());
195+
196+
if (itenr.iteen() && sr.itef()) || (itenr.aiteen() && sr.aitef()) {
197+
r.itenr().modify(|w| {
198+
w.set_iteen(false);
199+
w.set_aiteen(false);
200+
});
201+
r.icifr().modify(|w| {
202+
w.set_citef(true);
203+
w.set_caitef(true);
204+
});
205+
} else if (itenr.itlen() && sr.itlf()) || (itenr.aitlen() && sr.aitlf()) {
206+
r.itenr().modify(|w| {
207+
w.set_itlen(false);
208+
w.set_aitlen(false);
209+
});
210+
r.icifr().modify(|w| {
211+
w.set_citlf(true);
212+
w.set_caitlf(true);
213+
});
214+
} else if (itenr.ithen() && sr.ithf()) || (itenr.aithen() && sr.aithf()) {
215+
r.itenr().modify(|w| {
216+
w.set_ithen(false);
217+
w.set_aithen(false);
218+
});
219+
r.icifr().modify(|w| {
220+
w.set_cithf(true);
221+
w.set_caithf(true);
222+
});
223+
} else {
224+
return;
225+
}
226+
227+
compiler_fence(Ordering::SeqCst);
228+
WAKER.wake();
229+
}
230+
}

embassy-stm32/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub mod dac;
6868
pub mod dcmi;
6969
#[cfg(dsihost)]
7070
pub mod dsihost;
71+
#[cfg(dts)]
72+
pub mod dts;
7173
#[cfg(eth)]
7274
pub mod eth;
7375
#[cfg(feature = "exti")]

examples/stm32h5/src/bin/dts.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use defmt::*;
5+
use embassy_executor::Spawner;
6+
use embassy_stm32::dts::{Dts, InterruptHandler, SampleTime};
7+
use embassy_stm32::peripherals::DTS;
8+
use embassy_stm32::rcc::frequency;
9+
use embassy_stm32::{bind_interrupts, Config};
10+
use embassy_time::Timer;
11+
use {defmt_rtt as _, panic_probe as _};
12+
13+
bind_interrupts!(struct Irqs {
14+
DTS => InterruptHandler;
15+
});
16+
17+
#[embassy_executor::main]
18+
async fn main(_spawner: Spawner) {
19+
let mut config = Config::default();
20+
{
21+
use embassy_stm32::rcc::*;
22+
config.rcc.hsi = Some(HSIPrescaler::DIV1);
23+
config.rcc.csi = true;
24+
config.rcc.pll1 = Some(Pll {
25+
source: PllSource::HSI,
26+
prediv: PllPreDiv::DIV4,
27+
mul: PllMul::MUL25,
28+
divp: Some(PllDiv::DIV2),
29+
divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q
30+
divr: None,
31+
});
32+
config.rcc.pll2 = Some(Pll {
33+
source: PllSource::HSI,
34+
prediv: PllPreDiv::DIV4,
35+
mul: PllMul::MUL25,
36+
divp: None,
37+
divq: None,
38+
divr: Some(PllDiv::DIV4), // 100mhz
39+
});
40+
config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz
41+
config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz
42+
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
43+
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
44+
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
45+
config.rcc.voltage_scale = VoltageScale::Scale1;
46+
config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;
47+
}
48+
let p = embassy_stm32::init(config);
49+
50+
info!("Hello World!");
51+
52+
let sample_time = SampleTime::ClockCycles15;
53+
let mut dts = Dts::new(p.DTS, Irqs, sample_time);
54+
55+
let cal = Dts::factory_calibration();
56+
let convert_to_celsius = |raw_temp: u16| {
57+
let raw_temp = raw_temp as f32;
58+
let sample_time = (sample_time as u8) as f32;
59+
60+
let f = frequency::<DTS>().0 as f32;
61+
62+
let t0 = cal.t0 as f32;
63+
let fmt0 = cal.fmt0.0 as f32;
64+
let ramp_coeff = cal.ramp_coeff as f32;
65+
66+
((f * sample_time / raw_temp) - fmt0) / ramp_coeff + t0
67+
};
68+
69+
loop {
70+
let temp = dts.read().await;
71+
info!("Temp: {} degrees", convert_to_celsius(temp));
72+
Timer::after_millis(500).await;
73+
}
74+
}

0 commit comments

Comments
 (0)