diff --git a/src/pounder/timestamp.rs b/src/pounder/timestamp.rs index 2431634..0ce0bfd 100644 --- a/src/pounder/timestamp.rs +++ b/src/pounder/timestamp.rs @@ -1,13 +1,40 @@ ///! ADC sample timestamper using external Pounder reference clock. +///! +///! # Design +///! +///! The pounder timestamper utilizes the pounder SYNC_CLK output as a fast external reference clock +///! for recording a timestamp for each of the ADC samples. +///! +///! To accomplish this, a timer peripheral is configured to be driven by an external clock input. +///! Due to the limitations of clock frequencies allowed by the timer peripheral, the SYNC_CLK input +///! is divided by 4. This clock then clocks the timer peripheral in a free-running mode with an ARR +///! (max count register value) configured to overflow once per ADC sample batch. +///! +///! Once the timer is configured, an input capture is configured to record the timer count +///! register. The input capture is configured to utilize an internal trigger for the input capture. +///! The internal trigger is selected such that when a sample is generated on ADC0, the input +///! capture is simultaneously triggered. This results in the input capture triggering identically +///! to when the ADC samples the input. +///! +///! Once the input capture is properly configured, a DMA transfer is configured to collect all of +///! timestamps. The DMA transfer collects 1 timestamp for each ADC sample collected. In order to +///! avoid potentially losing a timestamp for a sample, the DMA transfer operates in double-buffer +///! mode. As soon as the DMA transfer completes, the hardware automatically swaps over to a second +///! buffer to continue capturing. This alleviates timing sensitivities of the DMA transfer +///! schedule. use stm32h7xx_hal as hal; use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; use crate::{timers, SAMPLE_BUFFER_SIZE}; +// Three buffers are required for double buffered mode - 2 are owned by the DMA stream and 1 is the +// working data provided to the application. These buffers must exist in a DMA-accessible memory +// region. Note that AXISRAM is not initialized on boot, so their initial contents are undefined. #[link_section = ".axisram.buffers"] static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3]; +/// Software unit to timestamp stabilizer ADC samples using an external pounder reference clock. pub struct Timestamper { next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, timer: timers::PounderTimestampTimer, @@ -20,6 +47,21 @@ pub struct Timestamper { } impl Timestamper { + /// Construct the pounder sample timestamper. + /// + /// # Note + /// The DMA is immediately configured after instantiation. It will not collect any samples + /// until the sample timer begins to cause input capture triggers. + /// + /// # Args + /// * `timestamp_timer` - The timer peripheral used for capturing timestamps from. + /// * `stream` - The DMA stream to use for collecting timestamps. + /// * `capture_channel` - The input capture channel for collecting timestamps. + /// * `sampling_timer` - The stabilizer ADC sampling timer. + /// * `_clock_input` - The input pin for the external clock from Pounder. + /// + /// # Returns + /// The new pounder timestamper in an operational state. pub fn new( mut timestamp_timer: timers::PounderTimestampTimer, stream: hal::dma::dma::Stream7, @@ -51,8 +93,8 @@ impl Timestamper { Transfer::init( stream, input_capture, - // Note(unsafe): The BUF[0] and BUF[1] is "owned" by this peripheral. - // It shall not be used anywhere else in the module. + // Note(unsafe): BUF[0] and BUF[1] are "owned" by this peripheral. + // They shall not be used anywhere else in the module. unsafe { &mut BUF[0] }, unsafe { Some(&mut BUF[1]) }, config, @@ -63,10 +105,14 @@ impl Timestamper { Self { timer: timestamp_timer, transfer: data_transfer, + + // Note(unsafe): BUF[2] is "owned" by this peripheral. It shall not be used anywhere + // else in the module. next_buffer: unsafe { Some(&mut BUF[2]) }, } } + /// Update the period of the underlying timestamp timer. pub fn update_period(&mut self, period: u16) { self.timer.set_period_ticks(period); } diff --git a/src/timers.rs b/src/timers.rs index 851e6b8..977eed6 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -1,6 +1,7 @@ ///! The sampling timer is used for managing ADC sampling and external reference timestamping. use super::hal; +/// The source of an input capture trigger. #[allow(dead_code)] pub enum CaptureTrigger { Input13 = 0b01, @@ -8,6 +9,7 @@ pub enum CaptureTrigger { TriggerInput = 0b11, } +/// The event that should generate an external trigger from the peripheral. #[allow(dead_code)] pub enum TriggerGenerator { Reset = 0b000, @@ -20,6 +22,7 @@ pub enum TriggerGenerator { Ch4Compare = 0b111, } +/// Selects the trigger source for the timer peripheral. #[allow(dead_code)] pub enum TriggerSource { Trigger0 = 0, @@ -28,6 +31,7 @@ pub enum TriggerSource { Trigger3 = 0b11, } +/// Prescalers for externally-supplied reference clocks. pub enum Prescaler { Div1 = 0b00, Div2 = 0b01, @@ -116,6 +120,8 @@ macro_rules! timer_channels { self.timer.resume(); } + /// Configure the timer peripheral to generate a trigger based on the provided + /// source. #[allow(dead_code)] pub fn generate_trigger(&mut self, source: TriggerGenerator) { let regs = unsafe { &*hal::stm32::$TY::ptr() }; @@ -125,6 +131,7 @@ macro_rules! timer_channels { } + /// Select a trigger source for the timer peripheral. #[allow(dead_code)] pub fn set_trigger_source(&mut self, source: TriggerSource) { let regs = unsafe { &*hal::stm32::$TY::ptr() };