From fc81f3d55db88f954dd64225cb6bc96a84cf563c Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 15 Dec 2020 14:34:14 +0100 Subject: [PATCH 1/5] Removing DMA support from DI0 timestamping --- src/digital_input_stamper.rs | 130 ++++++----------------------------- src/main.rs | 4 +- 2 files changed, 23 insertions(+), 111 deletions(-) diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index d4204f3..5843528 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -3,9 +3,6 @@ ///! This module provides a means of timestamping the rising edges of an external reference clock on ///! the DI0 with a timer value from TIM5. ///! -///! This module only supports input clocks on DI0 and may or may not utilize DMA to collect -///! timestamps. -///! ///! # 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 tick rate (PSC) and maximum count value @@ -13,12 +10,6 @@ ///! 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 -///! complete. Instead, when all samples are collected, the module pauses the DMA transfer and -///! 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 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. @@ -27,35 +18,18 @@ ///! It appears that DMA transfers can take a significant amount of time to disable (400ns) if they ///! are being prematurely stopped (such is the case here). As such, for a sample batch size of 1, ///! this can take up a significant amount of the total available processing time for the samples. -///! To avoid this, the module does not use DMA when the sample batch size is one. Instead, the -///! module manually checks for any captured timestamps from the timer capture channel manually. In -///! this mode, the maximum input clock frequency supported is equal to the configured sample rate. +///! This module checks for any captured timestamps from the timer capture channel manually. In +///! this mode, the maximum input clock frequency supported is dependant on the sampling rate and +///! batch size. ///! -///! There is a small window while the DMA buffers are swapped where a timestamp could potentially -///! be lost. To prevent this, the `acuire_buffer()` method should not be pre-empted. Any lost -///! timestamp will trigger an over-capture interrupt. -use super::{ - hal, timers, DmaConfig, PeripheralToMemory, Transfer, SAMPLE_BUFFER_SIZE, -}; - -// The DMA buffers must exist in a location where DMA can access. By default, RAM uses DTCM, which -// is off-limits to the normal DMA peripheral. Instead, we use AXISRAM. -#[link_section = ".axisram.buffers"] -static mut BUF: [[u32; SAMPLE_BUFFER_SIZE]; 2] = [[0; SAMPLE_BUFFER_SIZE]; 2]; +///! This module only supports DI0 for timestamping due to trigger constraints on the DIx pins. If +///! timestamping is desired in DI1, a separate timer + capture channel will be necessary. +use super::{hal, timers}; /// The timestamper for DI0 reference clock inputs. pub struct InputStamper { _di0_trigger: hal::gpio::gpioa::PA3>, - next_buffer: Option<&'static mut [u32; SAMPLE_BUFFER_SIZE]>, - transfer: Option< - Transfer< - hal::dma::dma::Stream6, - timers::tim5::Channel4InputCapture, - PeripheralToMemory, - &'static mut [u32; SAMPLE_BUFFER_SIZE], - >, - >, - capture_channel: Option, + capture_channel: timers::tim5::Channel4InputCapture, } impl InputStamper { @@ -63,100 +37,40 @@ impl InputStamper { /// /// # Args /// * `trigger` - The capture trigger input pin. - /// * `stream` - The DMA stream to use for collecting timestamps. /// * `timer_channel - The timer channel used for capturing timestamps. - /// * `batch_size` - The number of samples collected per processing batch. pub fn new( trigger: hal::gpio::gpioa::PA3>, - stream: hal::dma::dma::Stream6, timer_channel: timers::tim5::Channel4, - batch_size: usize, ) -> Self { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. let input_capture = timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4); - // 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. - let (transfer, input_capture) = if batch_size >= 2 { - input_capture.listen_dma(); - - // Set up the DMA transfer. - let dma_config = DmaConfig::default().memory_increment(true); - - let timestamp_transfer: Transfer<_, _, PeripheralToMemory, _> = - Transfer::init( - stream, - input_capture, - unsafe { &mut BUF[0] }, - None, - dma_config, - ); - (Some(timestamp_transfer), None) - } else { - (None, Some(input_capture)) - }; - Self { - next_buffer: unsafe { Some(&mut BUF[1]) }, - transfer, capture_channel: input_capture, _di0_trigger: trigger, } } - /// Start capture timestamps on DI0. + /// Start to 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(); - } + self.capture_channel.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() - .unwrap() - .next_transfer(next_buffer) - .unwrap(); - let valid_count = prev_buffer.len() - remaining_transfers; - - self.next_buffer.replace(prev_buffer); - - // Note that we likely didn't finish the transfer, so only return the number of - // 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() { - Some(stamp) => { - self.next_buffer.as_mut().unwrap()[0] = stamp; - &self.next_buffer.as_ref().unwrap()[..1] - } - None => &[], - } + /// Get the latest timestamp that has occurred. + /// + /// # Note + /// This function must be called sufficiently often. If an over-capture event occurs, this + /// function will panic, as this indicates a timestamp was inadvertently dropped. + /// + /// To prevent timestamp loss, the batch size and sampling rate must be adjusted such that at + /// most one timestamp will occur in each data processing cycle. + pub fn latest_timestamp(&mut self) -> Option { + if self.capture_channel.check_overcapture() { + panic!("DI0 timestamp overrun"); } + + self.capture_channel.latest_capture() } } diff --git a/src/main.rs b/src/main.rs index 308a4f0..5ae2b0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -844,9 +844,7 @@ const APP: () = { let trigger = gpioa.pa3.into_alternate_af2(); digital_input_stamper::InputStamper::new( trigger, - dma_streams.6, timestamp_timer_channels.ch4, - SAMPLE_BUFFER_SIZE, ) }; @@ -882,7 +880,7 @@ const APP: () = { c.resources.dacs.1.acquire_buffer(), ]; - let _timestamps = c.resources.input_stamper.acquire_buffer(); + let _timestamp = c.resources.input_stamper.latest_timestamp(); for channel in 0..adc_samples.len() { for sample in 0..adc_samples[0].len() { From 67b6990fc027d5e575d1cb1476405f9cd7dc6e2d Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 4 Jan 2021 17:12:24 +0100 Subject: [PATCH 2/5] Addressing PR review --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- openocd.gdb | 3 +++ src/design_parameters.rs | 6 +++-- src/digital_input_stamper.rs | 38 +++++++++++++++++++++++++++++-- src/main.rs | 43 ++++++++++-------------------------- src/timers.rs | 4 ++-- 7 files changed, 60 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edc2864..f7082af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,7 +517,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.8.0" -source = "git+https://github.com/quartiq/stm32h7xx-hal?branch=feature/number-of-transfers#e70a78788e74be5281321213b53e8cd1d213550e" +source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=dma#25ee0f3a9ae27d1fd6bb390d6045aa312f29f096" dependencies = [ "bare-metal 1.0.0", "cast", diff --git a/Cargo.toml b/Cargo.toml index f1acbe0..7217589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,8 +53,8 @@ default-features = false [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] -git = "https://github.com/quartiq/stm32h7xx-hal" -branch = "feature/number-of-transfers" +git = "https://github.com/stm32-rs/stm32h7xx-hal" +branch = "dma" [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] diff --git a/openocd.gdb b/openocd.gdb index e903a33..a96f8d4 100644 --- a/openocd.gdb +++ b/openocd.gdb @@ -18,6 +18,9 @@ load # tbreak cortex_m_rt::reset_handler monitor reset halt +source ../../PyCortexMDebug/cmdebug/svd_gdb.py +svd_load ~/Downloads/STM32H743x.svd + # cycle counter delta tool, place two bkpts around the section set var $cc=0xe0001004 define qq diff --git a/src/design_parameters.rs b/src/design_parameters.rs index 414a9e2..40be7b6 100644 --- a/src/design_parameters.rs +++ b/src/design_parameters.rs @@ -1,9 +1,11 @@ +use super::hal::time::MegaHertz; + /// The ADC setup time is the number of seconds after the CSn line goes low before the serial clock /// may begin. This is used for performing the internal ADC conversion. pub const ADC_SETUP_TIME: f32 = 220e-9; /// The maximum DAC/ADC serial clock line frequency. This is a hardware limit. -pub const ADC_DAC_SCK_MHZ_MAX: u32 = 50; +pub const ADC_DAC_SCK_MAX: MegaHertz = MegaHertz(50); /// The optimal counting frequency of the hardware timers used for timestamping and sampling. -pub const TIMER_FREQUENCY_MHZ: u32 = 100; +pub const TIMER_FREQUENCY: MegaHertz = MegaHertz(100); diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index 5843528..43ff9c5 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -24,7 +24,41 @@ ///! ///! This module only supports DI0 for timestamping due to trigger constraints on the DIx pins. If ///! timestamping is desired in DI1, a separate timer + capture channel will be necessary. -use super::{hal, timers}; +use super::{hal, timers, SAMPLE_BUFFER_SIZE, ADC_SAMPLE_TICKS}; + +/// Calculate the period of the digital input timestampe timer. +/// +/// # Note +/// The period returned will be 1 less than the required period in timer ticks. The value returned +/// can be immediately programmed into a hardware timer period register. +/// +/// The period is calcualted to be some power-of-two multiple of the batch size, such that N batches +/// will occur between each timestamp timer overflow. +/// +/// # Returns +/// A 32-bit value that can be programmed into a hardware timer period register. +pub fn calculate_timestamp_timer_period() -> u32 { + // Calculate how long a single batch requires in timer ticks. + let batch_duration_ticks: u64 = SAMPLE_BUFFER_SIZE as u64 * ADC_SAMPLE_TICKS as u64; + + // Calculate the largest power-of-two that is less than or equal to + // `batches_per_overflow`. This is completed by eliminating the least significant + // bits of the value until only the msb remains, which is always a power of two. + let batches_per_overflow: u64 = + (1u64 + u32::MAX as u64) / batch_duration_ticks; + let mut j = batches_per_overflow; + while (j & (j - 1)) != 0 { + j = j & (j - 1); + } + + // Once the number of batches per timestamp overflow is calculated, we can figure out the final + // period of the timestamp timer. The period is always 1 larger than the value configured in the + // register. + let period: u64 = batch_duration_ticks * j - 1u64; + assert!(period < u32::MAX as u64); + + period as u32 +} /// The timestamper for DI0 reference clock inputs. pub struct InputStamper { @@ -45,7 +79,7 @@ impl InputStamper { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. let input_capture = - timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4); + timer_channel.into_input_capture(timers::tim5::CC4S_A::TI4); Self { capture_channel: input_capture, diff --git a/src/main.rs b/src/main.rs index 5ae2b0f..52c5650 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,8 +30,6 @@ extern crate panic_halt; #[macro_use] extern crate log; -use core::convert::TryInto; - // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; use cortex_m_rt::exception; use rtic::cyccnt::{Instant, U32Ext}; @@ -294,10 +292,10 @@ const APP: () = { // Configure the timer to count at the designed tick rate. We will manually set the // period below. timer2.pause(); - timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY_MHZ.mhz()); + timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY); let mut sampling_timer = timers::SamplingTimer::new(timer2); - sampling_timer.set_period(ADC_SAMPLE_TICKS - 1); + sampling_timer.set_period_ticks(ADC_SAMPLE_TICKS - 1); sampling_timer }; @@ -313,32 +311,15 @@ const APP: () = { // Configure the timer to count at the designed tick rate. We will manually set the // period below. timer5.pause(); - timer5.set_tick_freq(design_parameters::TIMER_FREQUENCY_MHZ.mhz()); + timer5.set_tick_freq(design_parameters::TIMER_FREQUENCY); // The time stamp timer must run at exactly a multiple of the sample timer based on the - // batch size. To accomodate this, we manually set the period identical to the sample - // timer, but use a prescaler that is `BATCH_SIZE` longer. + // batch size. To accomodate this, we manually set the prescaler identical to the sample + // timer, but use a period that is longer. let mut timer = timers::TimestampTimer::new(timer5); - let period: u32 = { - let batch_duration: u64 = - SAMPLE_BUFFER_SIZE as u64 * ADC_SAMPLE_TICKS as u64; - let batches_per_overflow: u64 = - (1u64 + u32::MAX as u64) / batch_duration; - - // Calculate the largest power-of-two that is less than `batches_per_overflow`. - // This is completed by eliminating the least significant bits of the value until - // only the msb remains, which is always a power of two. - let mut j = batches_per_overflow; - while (j & (j - 1)) != 0 { - j = j & (j - 1); - } - - let period: u64 = batch_duration * j - 1u64; - period.try_into().unwrap() - }; - - timer.set_period(period); + let period = digital_input_stamper::calculate_timestamp_timer_period(); + timer.set_period_ticks(period); timer }; @@ -372,7 +353,7 @@ const APP: () = { let spi: hal::spi::Spi<_, _, u16> = dp.SPI2.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, - design_parameters::ADC_DAC_SCK_MHZ_MAX.mhz(), + design_parameters::ADC_DAC_SCK_MAX, ccdr.peripheral.SPI2, &ccdr.clocks, ); @@ -410,7 +391,7 @@ const APP: () = { let spi: hal::spi::Spi<_, _, u16> = dp.SPI3.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, - design_parameters::ADC_DAC_SCK_MHZ_MAX.mhz(), + design_parameters::ADC_DAC_SCK_MAX, ccdr.peripheral.SPI3, &ccdr.clocks, ); @@ -460,7 +441,7 @@ const APP: () = { dp.SPI4.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, - design_parameters::ADC_DAC_SCK_MHZ_MAX.mhz(), + design_parameters::ADC_DAC_SCK_MAX, ccdr.peripheral.SPI4, &ccdr.clocks, ) @@ -492,7 +473,7 @@ const APP: () = { dp.SPI5.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, - design_parameters::ADC_DAC_SCK_MHZ_MAX.mhz(), + design_parameters::ADC_DAC_SCK_MAX, ccdr.peripheral.SPI5, &ccdr.clocks, ) @@ -702,7 +683,7 @@ const APP: () = { // Ensure that we have enough time for an IO-update every sample. let sample_frequency = - (design_parameters::TIMER_FREQUENCY_MHZ as f32 + (design_parameters::TIMER_FREQUENCY.0 as f32 * 1_000_000.0) / ADC_SAMPLE_TICKS as f32; diff --git a/src/timers.rs b/src/timers.rs index 03bc0aa..8afa5cd 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -41,7 +41,7 @@ macro_rules! timer_channels { /// Manually set the period of the timer. #[allow(dead_code)] - pub fn set_period(&mut self, period: u32) { + pub fn set_period_ticks(&mut self, period: u32) { let regs = unsafe { &*hal::stm32::$TY::ptr() }; regs.arr.write(|w| w.arr().bits(period)); } @@ -136,7 +136,7 @@ macro_rules! timer_channels { /// # Args /// * `input` - The input source for the input capture event. #[allow(dead_code)] - pub fn to_input_capture(self, input: hal::stm32::tim2::[< $ccmrx _input >]::[< CC $index S_A >]) -> [< Channel $index InputCapture >]{ + pub fn into_input_capture(self, input: hal::stm32::tim2::[< $ccmrx _input >]::[< CC $index S_A >]) -> [< Channel $index InputCapture >]{ let regs = unsafe { &*<$TY>::ptr() }; regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input)); From 7ecd08d86bfca28725f8a22ff02f960032b7103e Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 4 Jan 2021 18:04:01 +0100 Subject: [PATCH 3/5] More updates after PR review --- src/design_parameters.rs | 15 +++++++++++++++ src/digital_input_stamper.rs | 13 ++++++------- src/main.rs | 35 ++++++++++++++++------------------- src/timers.rs | 12 ++++++++++-- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/design_parameters.rs b/src/design_parameters.rs index 40be7b6..125e133 100644 --- a/src/design_parameters.rs +++ b/src/design_parameters.rs @@ -9,3 +9,18 @@ pub const ADC_DAC_SCK_MAX: MegaHertz = MegaHertz(50); /// The optimal counting frequency of the hardware timers used for timestamping and sampling. pub const TIMER_FREQUENCY: MegaHertz = MegaHertz(100); + +/// The QSPI frequency for communicating with the pounder DDS. +pub const POUNDER_QSPI_FREQUENCY: MegaHertz = MegaHertz(40); + +/// The delay after initiating a QSPI transfer before asserting the IO_Update for the pounder DDS. +// Pounder Profile writes are always 16 bytes, with 2 cycles required per byte, coming out to a +// total of 32 QSPI clock cycles. The QSPI is configured for 40MHz, so this comes out to an offset +// of 800nS. We use 900ns to be safe. +pub const POUNDER_IO_UPDATE_DELAY: f32 = 900_e-9; + +/// The duration to assert IO_Update for the pounder DDS. +// IO_Update should be latched for 4 SYNC_CLK cycles after the QSPI profile write. With pounder +// SYNC_CLK running at 100MHz (1/4 of the pounder reference clock of 400MHz), this corresponds to +// 40ns. To accomodate rounding errors, we use 50ns instead. +pub const POUNDER_IO_UPDATE_DURATION: f32 = 50_e-9; diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index 43ff9c5..85f248f 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -24,7 +24,7 @@ ///! ///! This module only supports DI0 for timestamping due to trigger constraints on the DIx pins. If ///! timestamping is desired in DI1, a separate timer + capture channel will be necessary. -use super::{hal, timers, SAMPLE_BUFFER_SIZE, ADC_SAMPLE_TICKS}; +use super::{hal, timers, ADC_SAMPLE_TICKS, SAMPLE_BUFFER_SIZE}; /// Calculate the period of the digital input timestampe timer. /// @@ -39,7 +39,8 @@ use super::{hal, timers, SAMPLE_BUFFER_SIZE, ADC_SAMPLE_TICKS}; /// A 32-bit value that can be programmed into a hardware timer period register. pub fn calculate_timestamp_timer_period() -> u32 { // Calculate how long a single batch requires in timer ticks. - let batch_duration_ticks: u64 = SAMPLE_BUFFER_SIZE as u64 * ADC_SAMPLE_TICKS as u64; + let batch_duration_ticks: u64 = + SAMPLE_BUFFER_SIZE as u64 * ADC_SAMPLE_TICKS as u64; // Calculate the largest power-of-two that is less than or equal to // `batches_per_overflow`. This is completed by eliminating the least significant @@ -101,10 +102,8 @@ impl InputStamper { /// To prevent timestamp loss, the batch size and sampling rate must be adjusted such that at /// most one timestamp will occur in each data processing cycle. pub fn latest_timestamp(&mut self) -> Option { - if self.capture_channel.check_overcapture() { - panic!("DI0 timestamp overrun"); - } - - self.capture_channel.latest_capture() + self.capture_channel + .latest_capture() + .expect("DI0 timestamp overrun") } } diff --git a/src/main.rs b/src/main.rs index 52c5650..ede498f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -318,7 +318,8 @@ const APP: () = { // timer, but use a period that is longer. let mut timer = timers::TimestampTimer::new(timer5); - let period = digital_input_stamper::calculate_timestamp_timer_period(); + let period = + digital_input_stamper::calculate_timestamp_timer_period(); timer.set_period_ticks(period); timer @@ -543,7 +544,7 @@ const APP: () = { let qspi = hal::qspi::Qspi::bank2( dp.QUADSPI, qspi_pins, - 40.mhz(), + design_parameters::POUNDER_QSPI_FREQUENCY, &ccdr.clocks, ccdr.peripheral.QSPI, ); @@ -665,30 +666,26 @@ const APP: () = { ccdr.peripheral.HRTIM, ); - // IO_Update should be latched for 4 SYNC_CLK cycles after the QSPI profile - // write. With pounder SYNC_CLK running at 100MHz (1/4 of the pounder reference - // clock of 400MHz), this corresponds to 40ns. To accomodate rounding errors, we - // use 50ns instead. - // - // Profile writes are always 16 bytes, with 2 cycles required per byte, coming - // out to a total of 32 QSPI clock cycles. The QSPI is configured for 40MHz, so - // this comes out to an offset of 800nS. We use 900ns to be safe - note that the - // timer is triggered after the QSPI write, which can take approximately 120nS, - // so there is additional margin. + // IO_Update occurs after a fixed delay from the QSPI write. Note that the timer + // is triggered after the QSPI write, which can take approximately 120nS, so + // there is additional margin. hrtimer.configure_single_shot( hrtimer::Channel::Two, - 50_e-9, - 900_e-9, + design_parameters::POUNDER_IO_UPDATE_DURATION, + design_parameters::POUNDER_IO_UPDATE_DELAY, ); // Ensure that we have enough time for an IO-update every sample. - let sample_frequency = - (design_parameters::TIMER_FREQUENCY.0 as f32 - * 1_000_000.0) - / ADC_SAMPLE_TICKS as f32; + let sample_frequency = (design_parameters::TIMER_FREQUENCY.0 + as f32 + * 1_000_000.0) + / ADC_SAMPLE_TICKS as f32; let sample_period = 1.0 / sample_frequency; - assert!(sample_period > 900_e-9); + assert!( + sample_period + > design_parameters::POUNDER_IO_UPDATE_DELAY + ); hrtimer }; diff --git a/src/timers.rs b/src/timers.rs index 8afa5cd..5ffbeaf 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -147,17 +147,25 @@ macro_rules! timer_channels { impl [< Channel $index InputCapture >] { /// Get the latest capture from the channel. #[allow(dead_code)] - pub fn latest_capture(&mut self) -> Option { + pub fn latest_capture(&mut self) -> Result, ()> { // 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() }; let sr = regs.sr.read(); let ccx = regs.[< ccr $index >].read(); - if sr.[< cc $index if >]().bit_is_set() { + + let result = if sr.[< cc $index if >]().bit_is_set() { regs.sr.modify(|_, w| w.[< cc $index if >]().clear_bit()); Some(ccx.ccr().bits()) } else { None + }; + + // If there is an overcapture, return an error. + if sr.[< cc $index of >]().bit_is_clear() { + Ok(result) + } else { + Err(()) } } From 2b6e6f59a4e79195b1337cc51591cb64a4216223 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 4 Jan 2021 18:09:16 +0100 Subject: [PATCH 4/5] Adding comment about sample rate --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index ede498f..5979d90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,7 @@ 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. +// Currently, the sample rate is equal to: Fsample = 100/256 MHz = 390.625 KHz const ADC_SAMPLE_TICKS: u32 = 256; // The desired ADC sample processing buffer size. From 9e7bfd4371d50323287044142d7fd2a4ec4d96c4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 6 Jan 2021 12:24:09 +0100 Subject: [PATCH 5/5] Adding updates after review --- src/digital_input_stamper.rs | 2 +- src/main.rs | 9 +++++---- src/timers.rs | 11 +++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index 85f248f..910ae98 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -56,7 +56,7 @@ pub fn calculate_timestamp_timer_period() -> u32 { // period of the timestamp timer. The period is always 1 larger than the value configured in the // register. let period: u64 = batch_duration_ticks * j - 1u64; - assert!(period < u32::MAX as u64); + assert!(period <= u32::MAX as u64); period as u32 } diff --git a/src/main.rs b/src/main.rs index 5979d90..a0430d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -677,10 +677,11 @@ const APP: () = { ); // Ensure that we have enough time for an IO-update every sample. - let sample_frequency = (design_parameters::TIMER_FREQUENCY.0 - as f32 - * 1_000_000.0) - / ADC_SAMPLE_TICKS as f32; + let sample_frequency = { + let timer_frequency: hal::time::Hertz = + design_parameters::TIMER_FREQUENCY.into(); + timer_frequency.0 as f32 / ADC_SAMPLE_TICKS as f32 + }; let sample_period = 1.0 / sample_frequency; assert!( diff --git a/src/timers.rs b/src/timers.rs index 5ffbeaf..8d7d010 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -152,19 +152,22 @@ macro_rules! timer_channels { // Only atomic operations on completed on the timer registers. let regs = unsafe { &*<$TY>::ptr() }; let sr = regs.sr.read(); - let ccx = regs.[< ccr $index >].read(); let result = if sr.[< cc $index if >]().bit_is_set() { - regs.sr.modify(|_, w| w.[< cc $index if >]().clear_bit()); + // Read the capture value. Reading the captured value clears the flag in the + // status register automatically. + let ccx = regs.[< ccr $index >].read(); Some(ccx.ccr().bits()) } else { None }; - // If there is an overcapture, return an error. - if sr.[< cc $index of >]().bit_is_clear() { + // Read SR again to check for a potential over-capture. If there is an + // overcapture, return an error. + if regs.sr.read().[< cc $index of >]().bit_is_clear() { Ok(result) } else { + regs.sr.modify(|_, w| w.[< cc $index of >]().clear_bit()); Err(()) } }