Merge pull request #255 from quartiq/rj/timestamp-tweaks
Rj/timestamp tweaks
This commit is contained in:
commit
14abaad7de
@ -22,8 +22,9 @@ impl Vec5 {
|
||||
pub fn lowpass(f: f32, q: f32, k: f32) -> Self {
|
||||
// 3rd order Taylor approximation of sin and cos.
|
||||
let f = f * 2. * PI;
|
||||
let fsin = f - f * f * f / 6.;
|
||||
let fcos = 1. - f * f / 2.;
|
||||
let f2 = f * f * 0.5;
|
||||
let fcos = 1. - f2;
|
||||
let fsin = f * (1. - f2 / 3.);
|
||||
let alpha = fsin / (2. * q);
|
||||
// IIR uses Q2.30 fixed point
|
||||
let a0 = (1. + alpha) / (1 << IIR::SHIFT) as f32;
|
||||
|
@ -83,24 +83,13 @@ const APP: () = {
|
||||
}
|
||||
}
|
||||
|
||||
/// Main DSP processing routine for Stabilizer.
|
||||
/// Main DSP processing routine.
|
||||
///
|
||||
/// # Note
|
||||
/// Processing time for the DSP application code is bounded by the following constraints:
|
||||
/// See `dual-iir` for general notes on processing time and timing.
|
||||
///
|
||||
/// DSP application code starts after the ADC has generated a batch of samples and must be
|
||||
/// completed by the time the next batch of ADC samples has been acquired (plus the FIFO buffer
|
||||
/// time). If this constraint is not met, firmware will panic due to an ADC input overrun.
|
||||
///
|
||||
/// The DSP application code must also fill out the next DAC output buffer in time such that the
|
||||
/// DAC can switch to it when it has completed the current buffer. If this constraint is not met
|
||||
/// it's possible that old DAC codes will be generated on the output and the output samples will
|
||||
/// be delayed by 1 batch.
|
||||
///
|
||||
/// Because the ADC and DAC operate at the same rate, these two constraints actually implement
|
||||
/// the same time bounds, meeting one also means the other is also met.
|
||||
///
|
||||
/// TODO: document lockin
|
||||
/// This is an implementation of a externally (DI0) referenced PLL lockin on the ADC0 signal.
|
||||
/// It outputs either I/Q or power/phase on DAC0/DAC1. Data is normalized to full scale.
|
||||
/// PLL bandwidth, filter bandwidth, slope, and x/y or power/phase post-filters are available.
|
||||
#[task(binds=DMA1_STR4, resources=[adcs, dacs, iir_state, iir_ch, lockin, timestamper, pll], priority=2)]
|
||||
fn process(c: process::Context) {
|
||||
let adc_samples = [
|
||||
@ -117,8 +106,14 @@ const APP: () = {
|
||||
let iir_state = c.resources.iir_state;
|
||||
let lockin = c.resources.lockin;
|
||||
|
||||
let timestamp = c
|
||||
.resources
|
||||
.timestamper
|
||||
.latest_timestamp()
|
||||
.unwrap_or_else(|t| t) // Ignore timer capture overflows.
|
||||
.map(|t| t as i32);
|
||||
let (pll_phase, pll_frequency) = c.resources.pll.update(
|
||||
c.resources.timestamper.latest_timestamp().map(|t| t as i32),
|
||||
timestamp,
|
||||
22, // frequency settling time (log2 counter cycles), TODO: expose
|
||||
22, // phase settling time, TODO: expose
|
||||
);
|
||||
|
@ -154,7 +154,6 @@ pub fn setup(
|
||||
// Configure the timer to count at the designed tick rate. We will manually set the
|
||||
// period below.
|
||||
timer2.pause();
|
||||
timer2.reset_counter();
|
||||
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
||||
|
||||
let mut sampling_timer = timers::SamplingTimer::new(timer2);
|
||||
@ -213,13 +212,15 @@ pub fn setup(
|
||||
timer5.pause();
|
||||
timer5.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
||||
|
||||
// The timestamp timer must run at exactly a multiple of the sample timer based on the
|
||||
// batch size. To accomodate this, we manually set the prescaler identical to the sample
|
||||
// timer, but use a period that is longer.
|
||||
// The timestamp timer runs at the counter cycle period as the sampling timers.
|
||||
// To accomodate this, we manually set the prescaler identical to the sample
|
||||
// timer, but use maximum overflow period.
|
||||
let mut timer = timers::TimestampTimer::new(timer5);
|
||||
|
||||
let period = digital_input_stamper::calculate_timestamp_timer_period();
|
||||
timer.set_period_ticks(period);
|
||||
// TODO: Check hardware synchronization of timestamping and the sampling timers
|
||||
// for phase shift determinism.
|
||||
|
||||
timer.set_period_ticks(u32::MAX);
|
||||
|
||||
timer
|
||||
};
|
||||
|
@ -25,42 +25,6 @@
|
||||
///! 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 crate::{ADC_SAMPLE_TICKS, SAMPLE_BUFFER_SIZE};
|
||||
|
||||
/// Calculate the period of the digital input timestamp 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 calculated 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 {
|
||||
@ -98,15 +62,12 @@ impl InputStamper {
|
||||
/// 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.
|
||||
/// This function must be called at least as often as timestamps arrive.
|
||||
/// If an over-capture event occurs, this function will clear the overflow,
|
||||
/// and return a new timestamp of unknown recency an `Err()`.
|
||||
/// Note that this indicates at least one timestamp was inadvertently dropped.
|
||||
#[allow(dead_code)]
|
||||
pub fn latest_timestamp(&mut self) -> Option<u32> {
|
||||
self.capture_channel
|
||||
.latest_capture()
|
||||
.expect("DI0 timestamp overrun")
|
||||
pub fn latest_timestamp(&mut self) -> Result<Option<u32>, Option<u32>> {
|
||||
self.capture_channel.latest_capture()
|
||||
}
|
||||
}
|
||||
|
@ -281,28 +281,26 @@ macro_rules! timer_channels {
|
||||
impl [< Channel $index InputCapture >] {
|
||||
/// Get the latest capture from the channel.
|
||||
#[allow(dead_code)]
|
||||
pub fn latest_capture(&mut self) -> Result<Option<$size>, ()> {
|
||||
pub fn latest_capture(&mut self) -> Result<Option<$size>, Option<$size>> {
|
||||
// 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 result = if sr.[< cc $index if >]().bit_is_set() {
|
||||
let result = if regs.sr.read().[< cc $index if >]().bit_is_set() {
|
||||
// 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())
|
||||
Some(regs.[< ccr $index >].read().ccr().bits())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// 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 {
|
||||
if regs.sr.read().[< cc $index of >]().bit_is_set() {
|
||||
regs.sr.modify(|_, w| w.[< cc $index of >]().clear_bit());
|
||||
Err(())
|
||||
Err(result)
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user