diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index 621264d..d4204f3 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -8,10 +8,10 @@ ///! ///! # Design ///! An input capture channel is configured on DI0 and fed into TIM5's capture channel 4. TIM5 is -///! then run in a free-running mode with a configured frequency and period. Whenever an edge on DI0 -///! triggers, the current TIM5 counter value is captured and recorded as a timestamp. This timestamp can be -///! either directly read from the timer channel or can be collected asynchronously via DMA -///! collection. +///! then run in a free-running mode with a configured tick rate (PSC) and maximum count value +///! (ARR). Whenever an edge on DI0 triggers, the current TIM5 counter value is captured and +///! recorded as a timestamp. This timestamp can be either directly read from the timer channel or +///! can be collected asynchronously via DMA collection. ///! ///! When DMA is used for timestamp collection, a DMA transfer is configured to collect as many ///! timestamps as there are samples, but it is intended that this DMA transfer should never @@ -19,9 +19,9 @@ ///! checks to see how many timestamps were collected. These collected timestamps are then returned ///! for further processing. ///! -///! To prevent silently discarding timestamps, the TIM5 input capture over-capture interrupt is -///! used. Any over-capture event (which indicates an overwritten timestamp) then generates an ISR -///! which handles the over-capture. +///! To prevent silently discarding timestamps, the TIM5 input capture over-capture flag is +///! continually checked. Any over-capture event (which indicates an overwritten timestamp) then +///! triggers a panic to indicate the dropped timestamp so that design parameters can be adjusted. ///! ///! # Tradeoffs ///! It appears that DMA transfers can take a significant amount of time to disable (400ns) if they @@ -77,9 +77,6 @@ impl InputStamper { let input_capture = timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4); - // Listen for over-capture events, which indicates an over-run of DI0 timestamps. - input_capture.listen_overcapture(); - // For small batch sizes, the overhead of DMA can become burdensome to the point where // timing is not met. The DMA requires 500ns overhead, whereas a direct register read only // requires ~80ns. When batches of 2-or-greater are used, use a DMA-based approach. @@ -89,7 +86,7 @@ impl InputStamper { // Set up the DMA transfer. let dma_config = DmaConfig::default().memory_increment(true); - let mut timestamp_transfer: Transfer<_, _, PeripheralToMemory, _> = + let timestamp_transfer: Transfer<_, _, PeripheralToMemory, _> = Transfer::init( stream, input_capture, @@ -97,8 +94,6 @@ impl InputStamper { None, dma_config, ); - - timestamp_transfer.start(|_| {}); (Some(timestamp_transfer), None) } else { (None, Some(input_capture)) @@ -112,12 +107,29 @@ impl InputStamper { } } + /// Start capture timestamps on DI0. + pub fn start(&mut self) { + if let Some(transfer) = &mut self.transfer { + transfer.start(|capture_channel| { + capture_channel.enable(); + }); + } else { + self.capture_channel.as_mut().unwrap().enable(); + } + } + /// Get all of the timestamps that have occurred during the last processing cycle. pub fn acquire_buffer(&mut self) -> &[u32] { // If we are using DMA, finish the transfer and swap over buffers. if self.transfer.is_some() { let next_buffer = self.next_buffer.take().unwrap(); + self.transfer.as_mut().unwrap().pause(|channel| { + if channel.check_overcapture() { + panic!("DI0 timestamp overrun"); + } + }); + let (prev_buffer, _, remaining_transfers) = self .transfer .as_mut() @@ -132,6 +144,10 @@ impl InputStamper { // timestamps actually collected. &self.next_buffer.as_ref().unwrap()[..valid_count] } else { + if self.capture_channel.as_ref().unwrap().check_overcapture() { + panic!("DI0 timestamp overrun"); + } + // If we aren't using DMA, just manually check the input capture channel for a // timestamp. match self.capture_channel.as_mut().unwrap().latest_capture() { diff --git a/src/main.rs b/src/main.rs index 14505e8..a22fe1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,10 +58,10 @@ use heapless::{consts::*, String}; // The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is // equal to 10ns per tick. -const ADC_SAMPLE_TICKS: u32 = 128; +const ADC_SAMPLE_TICKS: u32 = 256; // The desired ADC sample processing buffer size. -const SAMPLE_BUFFER_SIZE: usize = 8; +const SAMPLE_BUFFER_SIZE: usize = 1; // The number of cascaded IIR biquads per channel. Select 1 or 2! const IIR_CASCADE_LENGTH: usize = 1; @@ -831,7 +831,7 @@ const APP: () = { // Utilize the cycle counter for RTIC scheduling. cp.DWT.enable_cycle_counter(); - let input_stamper = { + let mut input_stamper = { let trigger = gpioa.pa3.into_alternate_af2(); digital_input_stamper::InputStamper::new( trigger, @@ -844,6 +844,7 @@ const APP: () = { // Start sampling ADCs. sampling_timer.start(); timestamp_timer.start(); + input_stamper.start(); init::LateResources { afes: (afe0, afe1), @@ -1085,11 +1086,6 @@ const APP: () = { panic!("DAC1 output error"); } - #[task(binds = TIM5, priority = 3)] - fn di0(_: di0::Context) { - panic!("DI0 timestamp overrun"); - } - extern "C" { // hw interrupt handlers for RTIC to use for scheduling tasks // one per priority diff --git a/src/timers.rs b/src/timers.rs index 74b4731..03bc0aa 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -32,21 +32,6 @@ macro_rules! timer_channels { self.channels.take().unwrap() } - /// Get the prescaler of a timer. - #[allow(dead_code)] - pub fn get_prescaler(&self) -> u16 { - let regs = unsafe { &*hal::stm32::$TY::ptr() }; - regs.psc.read().psc().bits() + 1 - } - - /// Manually set the prescaler of the timer. - #[allow(dead_code)] - pub fn set_prescaler(&mut self, prescaler: u16) { - let regs = unsafe { &*hal::stm32::$TY::ptr() }; - assert!(prescaler >= 1); - regs.psc.write(|w| w.psc().bits(prescaler - 1)); - } - /// Get the period of the timer. #[allow(dead_code)] pub fn get_period(&self) -> u32 { @@ -176,20 +161,6 @@ macro_rules! timer_channels { } } - /// Listen for over-capture events on the timer channel. - /// - /// # Note - /// An over-capture event is when a previous capture was lost due to a new capture. - /// - /// "Listening" is equivalent to enabling the interrupt for the event. - #[allow(dead_code)] - pub fn listen_overcapture(&self) { - // Note(unsafe): This channel owns all access to the specific timer channel. - // Only atomic operations on completed on the timer registers. - let regs = unsafe { &*<$TY>::ptr() }; - regs.dier.modify(|_, w| w.[]().set_bit()); - } - /// Allow the channel to generate DMA requests. #[allow(dead_code)] pub fn listen_dma(&self) { @@ -198,6 +169,24 @@ macro_rules! timer_channels { let regs = unsafe { &*<$TY>::ptr() }; regs.dier.modify(|_, w| w.[< cc $index de >]().set_bit()); } + + /// Enable the input capture to begin capturing timer values. + #[allow(dead_code)] + pub fn enable(&mut self) { + // Note(unsafe): This channel owns all access to the specific timer channel. + // Only atomic operations on completed on the timer registers. + let regs = unsafe { &*<$TY>::ptr() }; + regs.ccer.modify(|_, w| w.[< cc $index e >]().set_bit()); + } + + /// Check if an over-capture event has occurred. + #[allow(dead_code)] + pub fn check_overcapture(&self) -> bool { + // Note(unsafe): This channel owns all access to the specific timer channel. + // Only atomic operations on completed on the timer registers. + let regs = unsafe { &*<$TY>::ptr() }; + regs.sr.read().[< cc $index of >]().bit_is_set() + } } // Note(unsafe): This manually implements DMA support for input-capture channels. This