aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorGabriel Smith <[email protected]>2025-01-03 10:41:04 -0500
committerGabriel Smith <[email protected]>2025-01-04 13:56:41 -0500
commit4c8ee8786f4b0e9307189c032c33beb00bb159cf (patch)
treeb407a33fad29ebf7dc2ab5fc8195ce816cb47f0c /embassy-stm32
parent2cc50bac444e8bbf081f2ffbea0170d507b9be71 (diff)
stm32: Implement reads of DTS peripheral
Only PCLK-driven operation is supported.
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/src/dts/mod.rs239
-rw-r--r--embassy-stm32/src/dts/tsel.rs51
-rw-r--r--embassy-stm32/src/lib.rs2
3 files changed, 292 insertions, 0 deletions
diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs
new file mode 100644
index 000000000..58d7cf841
--- /dev/null
+++ b/embassy-stm32/src/dts/mod.rs
@@ -0,0 +1,239 @@
1//! Digital Temperature Sensor (DTS)
2
3use core::future::poll_fn;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6
7use embassy_hal_internal::{into_ref, PeripheralRef};
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::interrupt::InterruptExt;
11use crate::peripherals::DTS;
12use crate::time::Hertz;
13use crate::{interrupt, pac, rcc, Peripheral};
14
15mod tsel;
16pub use tsel::TriggerSel;
17
18#[allow(missing_docs)]
19#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub enum SampleTime {
22 ClockCycles1 = 1,
23 ClockCycles2 = 2,
24 ClockCycles3 = 3,
25 ClockCycles4 = 4,
26 ClockCycles5 = 5,
27 ClockCycles6 = 6,
28 ClockCycles7 = 7,
29 ClockCycles8 = 8,
30 ClockCycles9 = 9,
31 ClockCycles10 = 10,
32 ClockCycles11 = 11,
33 ClockCycles12 = 12,
34 ClockCycles13 = 13,
35 ClockCycles14 = 14,
36 ClockCycles15 = 15,
37}
38
39#[non_exhaustive]
40#[derive(Clone, Copy, PartialEq, Eq, Debug)]
41/// Config
42pub struct Config {
43 /// Sample time
44 pub sample_time: SampleTime,
45 /// Trigger selection
46 pub trigger: TriggerSel,
47}
48
49impl Default for Config {
50 fn default() -> Self {
51 Self {
52 sample_time: SampleTime::ClockCycles1,
53 trigger: TriggerSel::Software,
54 }
55 }
56}
57
58/// The read-only factory calibration values used for converting a
59/// measurement to a temperature.
60#[derive(Clone, Debug)]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62pub struct FactoryCalibration {
63 /// The calibration temperature in degrees Celsius.
64 pub t0: u8,
65 /// The frequency at the calibration temperature.
66 pub fmt0: Hertz,
67 /// The ramp coefficient in Hertz per degree Celsius.
68 pub ramp_coeff: u16,
69}
70
71const MAX_DTS_CLK_FREQ: Hertz = Hertz::mhz(1);
72
73/// Digital temperature sensor driver.
74pub struct Dts<'d> {
75 _peri: PeripheralRef<'d, DTS>,
76}
77
78static WAKER: AtomicWaker = AtomicWaker::new();
79
80impl<'d> Dts<'d> {
81 /// Create a new temperature sensor driver.
82 pub fn new(
83 _peri: impl Peripheral<P = DTS> + 'd,
84 _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::DTS, InterruptHandler> + 'd,
85 config: Config,
86 ) -> Self {
87 into_ref!(_peri);
88 rcc::enable_and_reset::<DTS>();
89
90 let prescaler = rcc::frequency::<DTS>() / MAX_DTS_CLK_FREQ;
91
92 if prescaler > 127 {
93 panic!("DTS PCLK frequency must be less than 127 MHz.");
94 }
95
96 Self::regs().cfgr1().modify(|w| {
97 w.set_refclk_sel(false);
98 w.set_hsref_clk_div(prescaler as u8);
99 w.set_q_meas_opt(false);
100 // Software trigger
101 w.set_intrig_sel(0);
102 w.set_smp_time(config.sample_time as u8);
103 w.set_intrig_sel(config.trigger as u8);
104 w.set_start(true);
105 w.set_en(true);
106 });
107
108 interrupt::DTS.unpend();
109 unsafe { interrupt::DTS.enable() };
110
111 Self { _peri }
112 }
113
114 /// Reconfigure the driver.
115 pub fn set_config(&mut self, config: &Config) {
116 Self::regs().cfgr1().modify(|w| {
117 w.set_smp_time(config.sample_time as u8);
118 w.set_intrig_sel(config.trigger as u8);
119 });
120 }
121
122 /// Get the read-only factory calibration values used for converting a
123 /// measurement to a temperature.
124 pub fn factory_calibration() -> FactoryCalibration {
125 let t0valr1 = Self::regs().t0valr1().read();
126 let t0 = match t0valr1.t0() {
127 0 => 30,
128 1 => 130,
129 _ => unimplemented!(),
130 };
131 let fmt0 = Hertz::hz(t0valr1.fmt0() as u32 * 100);
132
133 let ramp_coeff = Self::regs().rampvalr().read().ramp_coeff();
134
135 FactoryCalibration { t0, fmt0, ramp_coeff }
136 }
137
138 /// Perform an asynchronous temperature measurement. The returned future can
139 /// be awaited to obtain the measurement.
140 ///
141 /// The future returned waits for the next measurement to complete.
142 ///
143 /// # Example
144 ///
145 /// ```no_run
146 /// use embassy_stm32::{bind_interrupts, dts};
147 /// use embassy_stm32::dts::Dts;
148 ///
149 /// bind_interrupts!(struct Irqs {
150 /// DTS => temp::InterruptHandler;
151 /// });
152 ///
153 /// # async {
154 /// # let p: embassy_stm32::Peripherals = todo!();
155 /// let mut dts = Dts::new(p.DTS, Irqs, Default::default());
156 /// let v: u16 = dts.read().await;
157 /// # };
158 /// ```
159 pub async fn read(&mut self) -> u16 {
160 let r = Self::regs();
161
162 r.itenr().modify(|w| w.set_iteen(true));
163
164 poll_fn(|cx| {
165 WAKER.register(cx.waker());
166 if r.itenr().read().iteen() {
167 Poll::Pending
168 } else {
169 Poll::Ready(r.dr().read().mfreq())
170 }
171 })
172 .await
173 }
174
175 /// Returns the last measurement made, if any.
176 ///
177 /// There is no guarantee that the measurement is recent or that a
178 /// measurement has ever completed.
179 pub fn read_immediate(&mut self) -> u16 {
180 Self::regs().dr().read().mfreq()
181 }
182
183 fn regs() -> pac::dts::Dts {
184 pac::DTS
185 }
186}
187
188impl<'d> Drop for Dts<'d> {
189 fn drop(&mut self) {
190 Self::regs().cfgr1().modify(|w| w.set_en(false));
191 rcc::disable::<DTS>();
192 }
193}
194
195/// Interrupt handler.
196pub struct InterruptHandler {
197 _private: (),
198}
199
200impl interrupt::typelevel::Handler<interrupt::typelevel::DTS> for InterruptHandler {
201 unsafe fn on_interrupt() {
202 let r = pac::DTS;
203 let (sr, itenr) = (r.sr().read(), r.itenr().read());
204
205 if (itenr.iteen() && sr.itef()) || (itenr.aiteen() && sr.aitef()) {
206 r.itenr().modify(|w| {
207 w.set_iteen(false);
208 w.set_aiteen(false);
209 });
210 r.icifr().modify(|w| {
211 w.set_citef(true);
212 w.set_caitef(true);
213 });
214 } else if (itenr.itlen() && sr.itlf()) || (itenr.aitlen() && sr.aitlf()) {
215 r.itenr().modify(|w| {
216 w.set_itlen(false);
217 w.set_aitlen(false);
218 });
219 r.icifr().modify(|w| {
220 w.set_citlf(true);
221 w.set_caitlf(true);
222 });
223 } else if (itenr.ithen() && sr.ithf()) || (itenr.aithen() && sr.aithf()) {
224 r.itenr().modify(|w| {
225 w.set_ithen(false);
226 w.set_aithen(false);
227 });
228 r.icifr().modify(|w| {
229 w.set_cithf(true);
230 w.set_caithf(true);
231 });
232 } else {
233 return;
234 }
235
236 compiler_fence(Ordering::SeqCst);
237 WAKER.wake();
238 }
239}
diff --git a/embassy-stm32/src/dts/tsel.rs b/embassy-stm32/src/dts/tsel.rs
new file mode 100644
index 000000000..99eab6dd8
--- /dev/null
+++ b/embassy-stm32/src/dts/tsel.rs
@@ -0,0 +1,51 @@
1/// Trigger selection for H5
2#[cfg(stm32h5)]
3#[derive(Debug, Copy, Clone, Eq, PartialEq)]
4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5pub enum TriggerSel {
6 /// Software triggering. Performs continuous measurements.
7 Software = 0,
8 /// LPTIM1 CH1
9 Lptim1 = 1,
10 /// LPTIM2 CH1
11 Lptim2 = 2,
12 /// LPTIM3 CH1
13 #[cfg(not(stm32h503))]
14 Lptim3 = 3,
15 /// EXTI13
16 Exti13 = 4,
17}
18
19/// Trigger selection for H7, except for H7R and H7S
20#[cfg(stm32h7)]
21#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub enum TriggerSel {
24 /// Software triggering. Performs continuous measurements.
25 Software = 0,
26 /// LPTIM1 OUT
27 Lptim1 = 1,
28 /// LPTIM2 OUT
29 Lptim2 = 2,
30 /// LPTIM3 OUT
31 Lptim3 = 3,
32 /// EXTI13
33 Exti13 = 4,
34}
35
36/// Trigger selection for H7R and H7S
37#[cfg(stm32h7rs)]
38#[derive(Debug, Copy, Clone, Eq, PartialEq)]
39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40pub enum TriggerSel {
41 /// Software triggering. Performs continuous measurements.
42 Software = 0,
43 /// LPTIM4 OUT
44 Lptim4 = 1,
45 /// LPTIM2 CH1
46 Lptim2 = 2,
47 /// LPTIM3 CH1
48 Lptim3 = 3,
49 /// EXTI13
50 Exti13 = 4,
51}
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index d04199d05..61da754c3 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -68,6 +68,8 @@ pub mod dac;
68pub mod dcmi; 68pub mod dcmi;
69#[cfg(dsihost)] 69#[cfg(dsihost)]
70pub mod dsihost; 70pub mod dsihost;
71#[cfg(dts)]
72pub mod dts;
71#[cfg(eth)] 73#[cfg(eth)]
72pub mod eth; 74pub mod eth;
73#[cfg(feature = "exti")] 75#[cfg(feature = "exti")]