stm32_hrtim/capture.rs
1use super::timer::{self, InstanceX};
2pub use super::timer::{Ch1, Ch2, ChExt};
3use crate::ext::{CptcrW, MasterExt, TimExt};
4use core::marker::PhantomData;
5
6pub struct Dma;
7pub struct NoDma;
8
9/// Type alias for the default capture for channel 1
10pub type HrCaptCh1<TIM, PSCL> = HrCapt<TIM, PSCL, Ch1, NoDma>;
11
12/// Type alias for the default capture for channel 2
13pub type HrCaptCh2<TIM, PSCL> = HrCapt<TIM, PSCL, Ch2, NoDma>;
14
15pub struct HrCapt<TIM, PSCL, CH, DMA> {
16 _x: PhantomData<(TIM, PSCL, CH, DMA)>,
17}
18
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[derive(Copy, Clone, Debug)]
21pub enum CountingDirection {
22 Up = 0,
23 #[cfg(feature = "hrtim_v2")]
24 Down = 1,
25}
26
27/// Implemented for
28/// * TIM's update event
29/// * EEVT1-10
30///
31/// TODO:
32/// * All neighbor timers CMP1, CPM2, OUT1_RST and OUT1_SET events
33pub trait CaptureEvent<TIM, PSCL> {
34 const BITS: u32;
35}
36
37/// Trait for capture channels used for capturing edges
38///
39/// ```
40/// let capture: HrCapt<_, _, _> = todo!();
41/// if capture.is_pending() {
42/// let (value, dir) = capture.get_last();
43/// capture.clear_interrupt();
44/// defmt::info!("Edge captured at counter value: {}, with: {}", value, dir);
45/// }
46/// ```
47///
48/// or alternatively
49///
50/// ```
51/// let capture: HrCapt<_, _, _> = todo!();
52/// if let Some((value, dir)) = capture.get() {
53/// defmt::info!("Edge captured at counter value: {}, with: {}", value, dir);
54/// }
55/// ```
56pub trait HrCapture {
57 /// Try to get the capture value
58 ///
59 /// Returns none if edge has been captured since last time
60 ///
61 /// NOTE: This function will use [`Self::is_pending`] to chech if there is a value available and
62 /// [`Self::clear_interrupt`] to clear it.
63 fn get(&mut self) -> Option<(u16, CountingDirection)> {
64 if self.is_pending() {
65 let value = self.get_last();
66 self.clear_interrupt();
67 Some(value)
68 } else {
69 None
70 }
71 }
72
73 /// Get number of ticks relative to beginning of up counting
74 ///
75 /// where captures during down counting count as negative (before the upcount)
76 ///
77 /// ```text
78 /// Counter
79 /// ---------------------------------- <--- period
80 /// \ ^ /
81 /// \ | /
82 /// \ | /
83 /// \ | /
84 /// Down count \ | / Up count
85 /// \|/
86 /// <-------------- 0 --------------> t
87 /// Negative result | positive result
88 /// ```
89 ///
90 /// NOTE: This function will use [`Self::is_pending`] to check if there is a value available and
91 /// [`Self::clear_interrupt`] to clear it.
92 fn get_signed(&mut self, period: u16) -> Option<i32> {
93 if self.is_pending() {
94 let value = self.get_last_signed(period);
95 self.clear_interrupt();
96 Some(value)
97 } else {
98 None
99 }
100 }
101
102 fn get_last(&self) -> (u16, CountingDirection);
103
104 /// Get number of ticks relative to beginning of up counting
105 ///
106 /// where captures during down counting count as negative (before the upcount)
107 ///
108 /// ```text
109 /// Counter
110 /// ---------------------------------- <--- period
111 /// \ ^ /
112 /// \ | /
113 /// \ | /
114 /// \ | /
115 /// Down count \ | / Up count
116 /// \|/
117 /// <-------------- 0 --------------> t
118 /// Negative result | positive result
119 /// ```
120 fn get_last_signed(&self, #[allow(unused_variables)] period: u16) -> i32 {
121 let (value, dir) = self.get_last();
122
123 // The capture counter always counts up and restarts at period
124 match dir {
125 CountingDirection::Up => i32::from(value),
126 #[cfg(feature = "hrtim_v2")]
127 CountingDirection::Down => i32::from(value) - i32::from(period),
128 }
129 }
130
131 fn clear_interrupt(&mut self);
132
133 fn is_pending(&self) -> bool;
134}
135
136pub fn dma_value_to_dir_and_value(x: u32) -> (u16, CountingDirection) {
137 let value = (x & 0xFFFF) as u16;
138 #[cfg(feature = "hrtim_v2")]
139 match x & (1 << 16) != 0 {
140 true => (value, CountingDirection::Down),
141 false => (value, CountingDirection::Up),
142 }
143
144 #[cfg(any(feature = "hrtim_v1", feature = "hrtim_v1_1"))]
145 (value, CountingDirection::Up)
146}
147
148pub fn dma_value_to_signed(x: u32, #[allow(unused_variables)] period: u16) -> i32 {
149 let (value, dir) = dma_value_to_dir_and_value(x);
150
151 // The capture counter always counts up and restarts at period
152 match dir {
153 CountingDirection::Up => i32::from(value),
154 #[cfg(feature = "hrtim_v2")]
155 CountingDirection::Down => i32::from(value) - i32::from(period),
156 }
157}
158
159impl<TIM: InstanceX, CH: ChExt, PSCL> HrCapt<TIM, PSCL, CH, NoDma> {
160 /// Add event to capture
161 ///
162 /// If multiple events are added, they will be ORed together meaning
163 /// that a capture will be trigger if any one of the events triggers
164 pub fn add_event<E: CaptureEvent<TIM, PSCL>>(&mut self, _event: &E) {
165 let tim = unsafe { &*TIM::ptr() };
166
167 // SAFETY: We are the only one with access to cptXYcr
168 unsafe {
169 tim.cptcr(CH::CH).modify(|r, w| w.bits(r.bits() | E::BITS));
170 }
171 }
172
173 /// Remove event to capture
174 pub fn remove_event<E: CaptureEvent<TIM, PSCL>>(&mut self, _event: &E) {
175 let tim = unsafe { &*TIM::ptr() };
176
177 // SAFETY: We are the only one with access to cptXYcr
178 unsafe {
179 tim.cptcr(CH::CH).modify(|r, w| w.bits(r.bits() & !E::BITS));
180 }
181 }
182
183 /// Force capture trigger now
184 pub fn trigger_now(&mut self) {
185 // SAFETY: We are the only one with access to cptXYcr
186 let tim = unsafe { &*TIM::ptr() };
187
188 tim.cptcr(CH::CH).modify(|_, w| w.set_swcpt());
189 }
190
191 // TODO: It would be sufficient to instead of hr_control only require exclusive access to the owning timer
192 // however that would be hard to do since typically the capture device is a field of that same timer.
193 // Would it make more sense to have this method directly on HrTim instead?
194 pub fn enable_interrupt(&mut self, enable: bool, _hr_control: &mut super::HrPwmControl) {
195 let tim = unsafe { &*TIM::ptr() };
196
197 tim.dier().modify(|_r, w| w.cptie(CH::CH as _).bit(enable));
198 }
199
200 pub fn enable_dma(self, _ch: timer::DmaChannel<TIM>) -> HrCapt<TIM, PSCL, CH, Dma> {
201 // SAFETY: We own the only instance of this timers dma channel, no one else can do this
202 let tim = unsafe { &*TIM::ptr() };
203 tim.dier().modify(|_r, w| w.cptde(CH::CH as _).set_bit());
204 HrCapt { _x: PhantomData }
205 }
206}
207
208impl<TIM: InstanceX, CH: ChExt, PSCL, DMA> HrCapture for HrCapt<TIM, PSCL, CH, DMA> {
209 fn get_last(&self) -> (u16, CountingDirection) {
210 let tim = unsafe { &*TIM::ptr() };
211 let data = tim.cptr(CH::CH).read();
212
213 #[cfg(feature = "hrtim_v2")]
214 let dir = match data.dir().bit() {
215 true => CountingDirection::Down,
216 false => CountingDirection::Up,
217 };
218 #[cfg(any(feature = "hrtim_v1", feature = "hrtim_v1_1"))]
219 let dir = CountingDirection::Up;
220
221 let value = data.cpt().bits();
222
223 (value, dir)
224 }
225
226 fn clear_interrupt(&mut self) {
227 let tim = unsafe { &*TIM::ptr() };
228
229 // No need for exclusive access since this is a write only register
230 tim.icr().write(|w| w.cptc(CH::CH as _).clear());
231 }
232
233 fn is_pending(&self) -> bool {
234 let tim = unsafe { &*TIM::ptr() };
235
236 // No need for exclusive access since this is a read only register
237 tim.isr().read().cpt(CH::CH as _).bit()
238 }
239}