Updating timestamper after testing
This commit is contained in:
parent
3886dab961
commit
645a1cd832
|
@ -8,10 +8,10 @@
|
||||||
///!
|
///!
|
||||||
///! # Design
|
///! # Design
|
||||||
///! An input capture channel is configured on DI0 and fed into TIM5's capture channel 4. TIM5 is
|
///! 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
|
///! then run in a free-running mode with a configured tick rate (PSC) and maximum count value
|
||||||
///! triggers, the current TIM5 counter value is captured and recorded as a timestamp. This timestamp can be
|
///! (ARR). Whenever an edge on DI0 triggers, the current TIM5 counter value is captured and
|
||||||
///! either directly read from the timer channel or can be collected asynchronously via DMA
|
///! recorded as a timestamp. This timestamp can be either directly read from the timer channel or
|
||||||
///! collection.
|
///! can be collected asynchronously via DMA collection.
|
||||||
///!
|
///!
|
||||||
///! When DMA is used for timestamp collection, a DMA transfer is configured to collect as many
|
///! 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
|
///! 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
|
///! checks to see how many timestamps were collected. These collected timestamps are then returned
|
||||||
///! for further processing.
|
///! for further processing.
|
||||||
///!
|
///!
|
||||||
///! To prevent silently discarding timestamps, the TIM5 input capture over-capture interrupt is
|
///! To prevent silently discarding timestamps, the TIM5 input capture over-capture flag is
|
||||||
///! used. Any over-capture event (which indicates an overwritten timestamp) then generates an ISR
|
///! continually checked. Any over-capture event (which indicates an overwritten timestamp) then
|
||||||
///! which handles the over-capture.
|
///! triggers a panic to indicate the dropped timestamp so that design parameters can be adjusted.
|
||||||
///!
|
///!
|
||||||
///! # Tradeoffs
|
///! # Tradeoffs
|
||||||
///! It appears that DMA transfers can take a significant amount of time to disable (400ns) if they
|
///! 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 =
|
let input_capture =
|
||||||
timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4);
|
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
|
// 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
|
// 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.
|
// 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.
|
// Set up the DMA transfer.
|
||||||
let dma_config = DmaConfig::default().memory_increment(true);
|
let dma_config = DmaConfig::default().memory_increment(true);
|
||||||
|
|
||||||
let mut timestamp_transfer: Transfer<_, _, PeripheralToMemory, _> =
|
let timestamp_transfer: Transfer<_, _, PeripheralToMemory, _> =
|
||||||
Transfer::init(
|
Transfer::init(
|
||||||
stream,
|
stream,
|
||||||
input_capture,
|
input_capture,
|
||||||
|
@ -97,8 +94,6 @@ impl InputStamper {
|
||||||
None,
|
None,
|
||||||
dma_config,
|
dma_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
timestamp_transfer.start(|_| {});
|
|
||||||
(Some(timestamp_transfer), None)
|
(Some(timestamp_transfer), None)
|
||||||
} else {
|
} else {
|
||||||
(None, Some(input_capture))
|
(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.
|
/// Get all of the timestamps that have occurred during the last processing cycle.
|
||||||
pub fn acquire_buffer(&mut self) -> &[u32] {
|
pub fn acquire_buffer(&mut self) -> &[u32] {
|
||||||
// If we are using DMA, finish the transfer and swap over buffers.
|
// If we are using DMA, finish the transfer and swap over buffers.
|
||||||
if self.transfer.is_some() {
|
if self.transfer.is_some() {
|
||||||
let next_buffer = self.next_buffer.take().unwrap();
|
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
|
let (prev_buffer, _, remaining_transfers) = self
|
||||||
.transfer
|
.transfer
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
@ -132,6 +144,10 @@ impl InputStamper {
|
||||||
// timestamps actually collected.
|
// timestamps actually collected.
|
||||||
&self.next_buffer.as_ref().unwrap()[..valid_count]
|
&self.next_buffer.as_ref().unwrap()[..valid_count]
|
||||||
} else {
|
} 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
|
// If we aren't using DMA, just manually check the input capture channel for a
|
||||||
// timestamp.
|
// timestamp.
|
||||||
match self.capture_channel.as_mut().unwrap().latest_capture() {
|
match self.capture_channel.as_mut().unwrap().latest_capture() {
|
||||||
|
|
12
src/main.rs
12
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
|
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
||||||
// equal to 10ns per tick.
|
// equal to 10ns per tick.
|
||||||
const ADC_SAMPLE_TICKS: u32 = 128;
|
const ADC_SAMPLE_TICKS: u32 = 256;
|
||||||
|
|
||||||
// The desired ADC sample processing buffer size.
|
// 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!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
@ -831,7 +831,7 @@ const APP: () = {
|
||||||
// Utilize the cycle counter for RTIC scheduling.
|
// Utilize the cycle counter for RTIC scheduling.
|
||||||
cp.DWT.enable_cycle_counter();
|
cp.DWT.enable_cycle_counter();
|
||||||
|
|
||||||
let input_stamper = {
|
let mut input_stamper = {
|
||||||
let trigger = gpioa.pa3.into_alternate_af2();
|
let trigger = gpioa.pa3.into_alternate_af2();
|
||||||
digital_input_stamper::InputStamper::new(
|
digital_input_stamper::InputStamper::new(
|
||||||
trigger,
|
trigger,
|
||||||
|
@ -844,6 +844,7 @@ const APP: () = {
|
||||||
// Start sampling ADCs.
|
// Start sampling ADCs.
|
||||||
sampling_timer.start();
|
sampling_timer.start();
|
||||||
timestamp_timer.start();
|
timestamp_timer.start();
|
||||||
|
input_stamper.start();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
afes: (afe0, afe1),
|
afes: (afe0, afe1),
|
||||||
|
@ -1085,11 +1086,6 @@ const APP: () = {
|
||||||
panic!("DAC1 output error");
|
panic!("DAC1 output error");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = TIM5, priority = 3)]
|
|
||||||
fn di0(_: di0::Context) {
|
|
||||||
panic!("DI0 timestamp overrun");
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// hw interrupt handlers for RTIC to use for scheduling tasks
|
// hw interrupt handlers for RTIC to use for scheduling tasks
|
||||||
// one per priority
|
// one per priority
|
||||||
|
|
|
@ -32,21 +32,6 @@ macro_rules! timer_channels {
|
||||||
self.channels.take().unwrap()
|
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.
|
/// Get the period of the timer.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn get_period(&self) -> u32 {
|
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 the channel to generate DMA requests.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn listen_dma(&self) {
|
pub fn listen_dma(&self) {
|
||||||
|
@ -198,6 +169,24 @@ macro_rules! timer_channels {
|
||||||
let regs = unsafe { &*<$TY>::ptr() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
regs.dier.modify(|_, w| w.[< cc $index de >]().set_bit());
|
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
|
// Note(unsafe): This manually implements DMA support for input-capture channels. This
|
||||||
|
|
Loading…
Reference in New Issue