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}