stabilizer/hardware/input_stamper.rs
1//! Digital Input 0 (DI0) reference clock timestamper
2//!
3//! This module provides a means of timestamping the rising edges of an external reference clock on
4//! the DI0 with a timer value from TIM5.
5//!
6//! # Design
7//! An input capture channel is configured on DI0 and fed into TIM5's capture channel 4. TIM5 is
8//! then run in a free-running mode with a configured tick rate (PSC) and maximum count value
9//! (ARR). Whenever an edge on DI0 triggers, the current TIM5 counter value is captured and
10//! recorded as a timestamp. This timestamp can be either directly read from the timer channel or
11//! can be collected asynchronously via DMA collection.
12//!
13//! To prevent silently discarding timestamps, the TIM5 input capture over-capture flag is
14//! continually checked. Any over-capture event (which indicates an overwritten timestamp) then
15//! triggers a panic to indicate the dropped timestamp so that design parameters can be adjusted.
16//!
17//! # Tradeoffs
18//! It appears that DMA transfers can take a significant amount of time to disable (400ns) if they
19//! are being prematurely stopped (such is the case here). As such, for a sample batch size of 1,
20//! this can take up a significant amount of the total available processing time for the samples.
21//! This module checks for any captured timestamps from the timer capture channel manually. In
22//! this mode, the maximum input clock frequency supported is dependant on the sampling rate and
23//! batch size.
24//!
25//! This module only supports DI0 for timestamping due to trigger constraints on the DIx pins. If
26//! timestamping is desired in DI1, a separate timer + capture channel will be necessary.
27use super::{
28 hal::gpio::{Alternate, gpioa::PA3},
29 timers,
30};
31
32/// The timestamper for DI0 reference clock inputs.
33pub struct InputStamper {
34 _di0_trigger: PA3<Alternate<2>>,
35 capture_channel: timers::tim5::Channel4InputCapture,
36}
37
38impl InputStamper {
39 /// Construct the DI0 input timestamper.
40 ///
41 /// # Args
42 /// * `trigger` - The capture trigger input pin.
43 /// * `timer_channel - The timer channel used for capturing timestamps.
44 pub fn new(
45 trigger: PA3<Alternate<2>>,
46 timer_channel: timers::tim5::Channel4,
47 ) -> Self {
48 // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the
49 // capture source.
50 let mut input_capture =
51 timer_channel.into_input_capture(timers::tim5::CaptureSource4::Ti4);
52
53 // Do not prescale the input capture signal - require 8 consecutive samples to record an
54 // incoming event - this prevents spurious glitches from triggering captures.
55 input_capture.configure_filter(timers::InputFilter::Div1N8);
56
57 Self {
58 capture_channel: input_capture,
59 _di0_trigger: trigger,
60 }
61 }
62
63 /// Start to capture timestamps on DI0.
64 #[allow(dead_code)]
65 pub fn start(&mut self) {
66 self.capture_channel.enable();
67 }
68
69 /// Get the latest timestamp that has occurred.
70 ///
71 /// # Note
72 /// This function must be called at least as often as timestamps arrive.
73 /// If an over-capture event occurs, this function will clear the overflow,
74 /// and return a new timestamp of unknown recency an `Err()`.
75 /// Note that this indicates at least one timestamp was inadvertently dropped.
76 #[allow(dead_code)]
77 pub fn latest_timestamp(&mut self) -> Result<Option<u32>, Option<u32>> {
78 self.capture_channel.latest_capture()
79 }
80}