Updating timestamper after testing

master
Ryan Summers 2020-12-08 16:14:27 +01:00
parent 3886dab961
commit 645a1cd832
3 changed files with 51 additions and 50 deletions

View File

@ -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() {

View File

@ -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

View File

@ -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.[<cc $index ie>]().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