diff --git a/.github/bors.toml b/.github/bors.toml index 7168739..722246d 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -3,5 +3,5 @@ delete_merged_branches = true status = [ "style", "test (stable)", - "compile (stable, false)", + "compile (stable)", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 851ef53..f4b66b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,22 +35,18 @@ jobs: compile: runs-on: ubuntu-latest - continue-on-error: ${{ matrix.optional }} + continue-on-error: ${{ matrix.toolchain == 'nightly' }} strategy: matrix: toolchain: [stable] features: [''] - optional: [false] include: - toolchain: beta features: '' - optional: false - toolchain: stable features: pounder_v1_1 - optional: false - toolchain: nightly features: nightly - optional: true steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/dsp/src/iir_int.rs b/dsp/src/iir_int.rs index daff43c..f0ce157 100644 --- a/dsp/src/iir_int.rs +++ b/dsp/src/iir_int.rs @@ -1,4 +1,4 @@ -use core::f32::consts::PI; +use core::f64::consts::PI; use serde::{Deserialize, Serialize}; /// Generic vector for integer IIR filter. @@ -19,7 +19,7 @@ impl Vec5 { /// /// # Returns /// 2nd-order IIR filter coefficients in the form [b0,b1,b2,a1,a2]. a0 is set to -1. - pub fn lowpass(f: f32, q: f32, k: f32) -> Self { + pub fn lowpass(f: f64, q: f64, k: f64) -> Self { // 3rd order Taylor approximation of sin and cos. let f = f * 2. * PI; let f2 = f * f * 0.5; @@ -27,10 +27,10 @@ impl Vec5 { 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; - let b0 = (k / 2. * (1. - fcos) / a0) as _; - let a1 = (2. * fcos / a0) as _; - let a2 = ((alpha - 1.) / a0) as _; + let a0 = (1. + alpha) / (1 << IIR::SHIFT) as f64; + let b0 = (k / 2. * (1. - fcos) / a0 + 0.5) as _; + let a1 = (2. * fcos / a0 + 0.5) as _; + let a2 = ((alpha - 1.) / a0 + 0.5) as _; Self([b0, 2 * b0, b0, a1, a2]) } @@ -97,7 +97,7 @@ mod test { #[test] fn lowpass_gen() { - let ba = Vec5::lowpass(1e-3, 1. / 2f32.sqrt(), 2.); + let ba = Vec5::lowpass(1e-5, 1. / 2f64.sqrt(), 2.); println!("{:?}", ba.0); } } diff --git a/dsp/src/rpll.rs b/dsp/src/rpll.rs index d4bd86f..e312393 100644 --- a/dsp/src/rpll.rs +++ b/dsp/src/rpll.rs @@ -94,7 +94,6 @@ mod test { struct Harness { rpll: RPLL, - dt2: u8, shift_frequency: u8, shift_phase: u8, noise: i32, @@ -109,7 +108,6 @@ mod test { fn default() -> Self { Self { rpll: RPLL::new(8), - dt2: 8, shift_frequency: 9, shift_phase: 8, noise: 0, @@ -122,7 +120,7 @@ mod test { } fn run(&mut self, n: usize) -> (Vec, Vec) { - assert!(self.period >= 1 << self.dt2); + assert!(self.period >= 1 << self.rpll.dt2); assert!(self.period < 1 << self.shift_frequency); assert!(self.period < 1 << self.shift_phase + 1); @@ -130,7 +128,7 @@ mod test { let mut f = Vec::::new(); for _ in 0..n { let timestamp = if self.time - self.next_noisy >= 0 { - assert!(self.time - self.next_noisy < 1 << self.dt2); + assert!(self.time - self.next_noisy < 1 << self.rpll.dt2); self.next = self.next.wrapping_add(self.period); let timestamp = self.next_noisy; let p_noise = self.rng.gen_range(-self.noise..=self.noise); @@ -151,23 +149,23 @@ mod test { // phase error y.push(yi.wrapping_sub(y_ref) as f32 / 2f32.powi(32)); - let p_ref = 1 << 32 + self.dt2; + let p_ref = 1 << 32 + self.rpll.dt2; let p_sig = fi as u64 * self.period as u64; // relative frequency error f.push( p_sig.wrapping_sub(p_ref) as i64 as f32 - / 2f32.powi(32 + self.dt2 as i32), + / 2f32.powi(32 + self.rpll.dt2 as i32), ); // advance time - self.time = self.time.wrapping_add(1 << self.dt2); + self.time = self.time.wrapping_add(1 << self.rpll.dt2); } (y, f) } fn measure(&mut self, n: usize, limits: [f32; 4]) { - let t_settle = (1 << self.shift_frequency - self.dt2 + 4) - + (1 << self.shift_phase - self.dt2 + 4); + let t_settle = (1 << self.shift_frequency - self.rpll.dt2 + 4) + + (1 << self.shift_phase - self.rpll.dt2 + 4); self.run(t_settle); let (y, f) = self.run(n); @@ -268,4 +266,18 @@ mod test { h.measure(1 << 16, [2e-4, 6e-3, 2e-4, 2e-3]); } + + #[test] + fn batch_fast_narrow() { + let mut h = Harness::default(); + h.rpll.dt2 = 8 + 3; + h.period = 2431; + h.next = 35281; + h.next_noisy = h.next; + h.noise = 100; + h.shift_frequency = 23; + h.shift_phase = 23; + + h.measure(1 << 16, [1e-8, 2e-5, 6e-4, 6e-4]); + } } diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index d3b3410..74ca836 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -4,30 +4,13 @@ use stm32h7xx_hal as hal; -#[macro_use] -extern crate log; +use stabilizer::{hardware, hardware::design_parameters}; -use rtic::cyccnt::{Instant, U32Ext}; - -use heapless::{consts::*, String}; - -use stabilizer::{ - hardware, server, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2, -}; - -use dsp::{iir, iir_int, lockin::Lockin, rpll::RPLL, Accu}; +use dsp::{iir_int, lockin::Lockin, rpll::RPLL, Accu}; use hardware::{ Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1, }; -const SCALE: f32 = i16::MAX as _; - -const TCP_RX_BUFFER_SIZE: usize = 8192; -const TCP_TX_BUFFER_SIZE: usize = 8192; - -// The number of cascaded IIR biquads per channel. Select 1 or 2! -const IIR_CASCADE_LENGTH: usize = 1; - #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] const APP: () = { struct Resources { @@ -36,12 +19,6 @@ const APP: () = { dacs: (Dac0Output, Dac1Output), net_interface: hardware::Ethernet, - // Format: iir_state[ch][cascade-no][coeff] - #[init([[iir::Vec5([0.; 5]); IIR_CASCADE_LENGTH]; 2])] - iir_state: [[iir::Vec5; IIR_CASCADE_LENGTH]; 2], - #[init([[iir::IIR::new(1./(1 << 16) as f32, -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2])] - iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], - timestamper: InputStamper, pll: RPLL, lockin: Lockin, @@ -52,7 +29,10 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let pll = RPLL::new(ADC_SAMPLE_TICKS_LOG2 + SAMPLE_BUFFER_SIZE_LOG2); + let pll = RPLL::new( + design_parameters::ADC_SAMPLE_TICKS_LOG2 + + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, + ); let lockin = Lockin::new( iir_int::Vec5::lowpass(1e-3, 0.707, 2.), // TODO: expose @@ -92,7 +72,7 @@ const APP: () = { /// 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)] + #[task(binds=DMA1_STR4, resources=[adcs, dacs, lockin, timestamper, pll], priority=2)] fn process(c: process::Context) { let adc_samples = [ c.resources.adcs.0.acquire_buffer(), @@ -104,30 +84,30 @@ const APP: () = { c.resources.dacs.1.acquire_buffer(), ]; - let iir_ch = c.resources.iir_ch; - 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. + .unwrap_or(None) // Ignore data from timer capture overflows. .map(|t| t as i32); let (pll_phase, pll_frequency) = c.resources.pll.update( timestamp, - 22, // frequency settling time (log2 counter cycles), TODO: expose - 22, // phase settling time, TODO: expose + 21, // frequency settling time (log2 counter cycles), TODO: expose + 21, // phase settling time, TODO: expose ); // Harmonic index of the LO: -1 to _de_modulate the fundamental (complex conjugate) let harmonic: i32 = -1; // TODO: expose - // Demodulation LO phase offset + + // Demodulation LO phase offset let phase_offset: i32 = 0; // TODO: expose let sample_frequency = ((pll_frequency - // .wrapping_add(1 << SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias - >> SAMPLE_BUFFER_SIZE_LOG2) as i32) + // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias + >> design_parameters::SAMPLE_BUFFER_SIZE_LOG2) + as i32) .wrapping_mul(harmonic); let sample_phase = phase_offset.wrapping_add(pll_phase.wrapping_mul(harmonic)); @@ -142,187 +122,26 @@ const APP: () = { .last() .unwrap(); - // convert i/q to power/phase, - let power_phase = true; // TODO: expose - - let mut output = if power_phase { + let conf = "frequency_discriminator"; + let output = match conf { // Convert from IQ to power and phase. - [output.abs_sqr() as _, output.arg() as _] - } else { - [output.0 as _, output.1 as _] + "power_phase" => [output.abs_sqr(), output.arg()], + "frequency_discriminator" => [pll_frequency as i32, output.arg()], + _ => [output.0, output.1], }; - // Filter power and phase through IIR filters. - // Note: Normalization to be done in filters. Phase will wrap happily. - for j in 0..iir_state[0].len() { - for k in 0..output.len() { - output[k] = - iir_ch[k][j].update(&mut iir_state[k][j], output[k]); - } - } - - // Note(unsafe): range clipping to i16 is ensured by IIR filters above. // Convert to DAC data. for i in 0..dac_samples[0].len() { - unsafe { - dac_samples[0][i] = - output[0].to_int_unchecked::() as u16 ^ 0x8000; - dac_samples[1][i] = - output[1].to_int_unchecked::() as u16 ^ 0x8000; - } + dac_samples[0][i] = (output[0] >> 16) as u16 ^ 0x8000; + dac_samples[1][i] = (output[1] >> 16) as u16 ^ 0x8000; } } - #[idle(resources=[net_interface, iir_state, iir_ch, afes])] - fn idle(mut c: idle::Context) -> ! { - let mut socket_set_entries: [_; 8] = Default::default(); - let mut sockets = - smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]); - - let mut rx_storage = [0; TCP_RX_BUFFER_SIZE]; - let mut tx_storage = [0; TCP_TX_BUFFER_SIZE]; - let tcp_handle = { - let tcp_rx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); - let tcp_tx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); - let tcp_socket = - smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - sockets.add(tcp_socket) - }; - - let mut server = server::Server::new(); - - let mut time = 0u32; - let mut next_ms = Instant::now(); - - // TODO: Replace with reference to CPU clock from CCDR. - next_ms += 400_000.cycles(); - + #[idle(resources=[afes])] + fn idle(_: idle::Context) -> ! { loop { - let tick = Instant::now() > next_ms; - - if tick { - next_ms += 400_000.cycles(); - time += 1; - } - - { - let socket = - &mut *sockets.get::(tcp_handle); - if socket.state() == smoltcp::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1235) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else { - server.poll(socket, |req| { - info!("Got request: {:?}", req); - stabilizer::route_request!(req, - readable_attributes: [ - "stabilizer/iir/state": (|| { - let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][0].0[0], - y0: iir_state[0][0].0[2], - x1: iir_state[1][0].0[0], - y1: iir_state[1][0].0[2], - }); - - Ok::(state) - }), - // "_b" means cascades 2nd IIR - "stabilizer/iir_b/state": (|| { let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][IIR_CASCADE_LENGTH-1].0[0], - y0: iir_state[0][IIR_CASCADE_LENGTH-1].0[2], - x1: iir_state[1][IIR_CASCADE_LENGTH-1].0[0], - y1: iir_state[1][IIR_CASCADE_LENGTH-1].0[2], - }); - - Ok::(state) - }), - "stabilizer/afe0/gain": (|| c.resources.afes.0.get_gain()), - "stabilizer/afe1/gain": (|| c.resources.afes.1.get_gain()) - ], - - modifiable_attributes: [ - "stabilizer/iir0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b1/state": server::IirRequest,(|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/afe0/gain": hardware::AfeGain, (|gain| { - c.resources.afes.0.set_gain(gain); - Ok::<(), ()>(()) - }), - "stabilizer/afe1/gain": hardware::AfeGain, (|gain| { - c.resources.afes.1.set_gain(gain); - Ok::<(), ()>(()) - }) - ] - ) - }); - } - } - - let sleep = match c.resources.net_interface.poll( - &mut sockets, - smoltcp::time::Instant::from_millis(time as i64), - ) { - Ok(changed) => !changed, - Err(smoltcp::Error::Unrecognized) => true, - Err(e) => { - info!("iface poll error: {:?}", e); - true - } - }; - - if sleep { - cortex_m::asm::wfi(); - } + // TODO: Implement network interface. + cortex_m::asm::wfi(); } } diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index 4974be2..cadf189 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -4,13 +4,13 @@ use dsp::{iir_int, lockin::Lockin, Accu}; use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; -use stabilizer::{hardware, SAMPLE_BUFFER_SIZE, SAMPLE_BUFFER_SIZE_LOG2}; +use stabilizer::{hardware, hardware::design_parameters}; // A constant sinusoid to send on the DAC output. // Full-scale gives a +/- 10V amplitude waveform. Scale it down to give +/- 1V. const ONE: i16 = (0.1 * u16::MAX as f32) as _; const SQRT2: i16 = (ONE as f32 * 0.707) as _; -const DAC_SEQUENCE: [i16; SAMPLE_BUFFER_SIZE] = +const DAC_SEQUENCE: [i16; design_parameters::SAMPLE_BUFFER_SIZE] = [ONE, SQRT2, 0, -SQRT2, -ONE, -SQRT2, 0, SQRT2]; #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] @@ -83,7 +83,8 @@ const APP: () = { // Reference phase and frequency are known. let pll_phase = 0; - let pll_frequency = 1i32 << (32 - SAMPLE_BUFFER_SIZE_LOG2); + let pll_frequency = + 1i32 << (32 - design_parameters::SAMPLE_BUFFER_SIZE_LOG2); // Harmonic index of the LO: -1 to _de_modulate the fundamental let harmonic: i32 = -1; diff --git a/src/hardware/adc.rs b/src/hardware/adc.rs index 1cb6c17..3f2fa5d 100644 --- a/src/hardware/adc.rs +++ b/src/hardware/adc.rs @@ -74,9 +74,9 @@ ///! double-buffered mode offers less overhead due to the DMA disable/enable procedure). use stm32h7xx_hal as hal; -use crate::SAMPLE_BUFFER_SIZE; - +use super::design_parameters::SAMPLE_BUFFER_SIZE; use super::timers; + use hal::dma::{ config::Priority, dma::{DMAReq, DmaConfig}, diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index e173dc7..52db011 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -1,14 +1,6 @@ ///! Stabilizer hardware configuration ///! ///! This file contains all of the hardware-specific configuration of Stabilizer. -use crate::ADC_SAMPLE_TICKS; - -#[cfg(feature = "pounder_v1_1")] -use crate::SAMPLE_BUFFER_SIZE; - -#[cfg(feature = "pounder_v1_1")] -use core::convert::TryInto; - use smoltcp::{iface::Routes, wire::Ipv4Address}; use stm32h7xx_hal::{ @@ -157,7 +149,8 @@ pub fn setup( timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY); let mut sampling_timer = timers::SamplingTimer::new(timer2); - sampling_timer.set_period_ticks((ADC_SAMPLE_TICKS - 1) as u32); + sampling_timer + .set_period_ticks((design_parameters::ADC_SAMPLE_TICKS - 1) as u32); // The sampling timer is used as the master timer for the shadow-sampling timer. Thus, // it generates a trigger whenever it is enabled. @@ -181,7 +174,8 @@ pub fn setup( let mut shadow_sampling_timer = timers::ShadowSamplingTimer::new(timer3); - shadow_sampling_timer.set_period_ticks(ADC_SAMPLE_TICKS - 1); + shadow_sampling_timer + .set_period_ticks(design_parameters::ADC_SAMPLE_TICKS - 1); // The shadow sampling timer is a slave-mode timer to the sampling timer. It should // always be in-sync - thus, we configure it to operate in slave mode using "Trigger @@ -603,7 +597,7 @@ pub fn setup( let ref_clk: hal::time::Hertz = design_parameters::DDS_REF_CLK.into(); - let ad9959 = ad9959::Ad9959::new( + let mut ad9959 = ad9959::Ad9959::new( qspi_interface, reset_pin, &mut io_update, @@ -614,6 +608,8 @@ pub fn setup( ) .unwrap(); + ad9959.self_test().unwrap(); + // Return IO_Update gpiog.pg7 = io_update.into_analog(); @@ -726,7 +722,8 @@ pub fn setup( let sample_frequency = { let timer_frequency: hal::time::Hertz = design_parameters::TIMER_FREQUENCY.into(); - timer_frequency.0 as f32 / ADC_SAMPLE_TICKS as f32 + timer_frequency.0 as f32 + / design_parameters::ADC_SAMPLE_TICKS as f32 }; let sample_period = 1.0 / sample_frequency; @@ -761,22 +758,13 @@ pub fn setup( // Pounder is configured to generate a 500MHz reference clock, so a 125MHz sync-clock is // output. As a result, dividing the 125MHz sync-clk provides a 31.25MHz tick rate for // the timestamp timer. 31.25MHz corresponds with a 32ns tick rate. + // This is less than fCK_INT/3 of the timer as required for oversampling the trigger. timestamp_timer.set_external_clock(timers::Prescaler::Div4); timestamp_timer.start(); - // We want the pounder timestamp timer to overflow once per batch. - let tick_ratio = { - let sync_clk_mhz: f32 = design_parameters::DDS_SYSTEM_CLK.0 - as f32 - / design_parameters::DDS_SYNC_CLK_DIV as f32; - sync_clk_mhz / design_parameters::TIMER_FREQUENCY.0 as f32 - }; - - let period = (tick_ratio - * ADC_SAMPLE_TICKS as f32 - * SAMPLE_BUFFER_SIZE as f32) as u32 - / 4; - timestamp_timer.set_period_ticks((period - 1).try_into().unwrap()); + // Set the timer to wrap at the u16 boundary to meet the PLL periodicity. + // Scale and wrap before or after the PLL. + timestamp_timer.set_period_ticks(u16::MAX); let tim8_channels = timestamp_timer.channels(); pounder::timestamp::Timestamper::new( diff --git a/src/hardware/dac.rs b/src/hardware/dac.rs index 5ca65fb..d41ae8c 100644 --- a/src/hardware/dac.rs +++ b/src/hardware/dac.rs @@ -52,9 +52,9 @@ ///! served promptly after the transfer completes. use stm32h7xx_hal as hal; -use crate::SAMPLE_BUFFER_SIZE; - +use super::design_parameters::SAMPLE_BUFFER_SIZE; use super::timers; + use hal::dma::{ dma::{DMAReq, DmaConfig}, traits::TargetAddress, diff --git a/src/hardware/design_parameters.rs b/src/hardware/design_parameters.rs index 3de7c15..ddc0614 100644 --- a/src/hardware/design_parameters.rs +++ b/src/hardware/design_parameters.rs @@ -39,3 +39,13 @@ pub const DDS_SYSTEM_CLK: MegaHertz = /// The divider from the DDS system clock to the SYNC_CLK output (sync-clk is always 1/4 of sysclk). #[allow(dead_code)] pub const DDS_SYNC_CLK_DIV: u8 = 4; + +// 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/128 MHz ~ 800 KHz +pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7; +pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; + +// The desired ADC sample processing buffer size. +pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; +pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2; diff --git a/src/hardware/digital_input_stamper.rs b/src/hardware/digital_input_stamper.rs index 1aa0bb1..baddeba 100644 --- a/src/hardware/digital_input_stamper.rs +++ b/src/hardware/digital_input_stamper.rs @@ -44,9 +44,13 @@ impl InputStamper { ) -> Self { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. - let input_capture = + let mut input_capture = timer_channel.into_input_capture(timers::tim5::CaptureSource4::TI4); + // Do not prescale the input capture signal - require 8 consecutive samples to record an + // incoming event - this prevents spurious glitches from triggering captures. + input_capture.configure_filter(timers::InputFilter::Div1N8); + Self { capture_channel: input_capture, _di0_trigger: trigger, diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index f912057..41bca1b 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -11,10 +11,10 @@ mod adc; mod afe; mod configuration; mod dac; -mod design_parameters; +pub mod design_parameters; mod digital_input_stamper; mod eeprom; -mod pounder; +pub mod pounder; mod timers; pub use adc::{Adc0Input, Adc1Input}; diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 3a85937..0497a37 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -mod attenuators; +pub mod attenuators; mod dds_output; pub mod hrtimer; mod rf_power; diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 7e39a94..0c06192 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -26,7 +26,7 @@ use stm32h7xx_hal as hal; use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; -use crate::{hardware::timers, SAMPLE_BUFFER_SIZE}; +use crate::hardware::{design_parameters::SAMPLE_BUFFER_SIZE, timers}; // 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 diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index e2cbbc8..78d27b6 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -48,6 +48,13 @@ pub enum SlaveMode { Trigger = 0b0110, } +/// Optional input capture preconditioning filter configurations. +#[allow(dead_code)] +pub enum InputFilter { + Div1N1 = 0b0000, + Div1N8 = 0b0011, +} + macro_rules! timer_channels { ($name:ident, $TY:ident, $size:ty) => { paste::paste! { @@ -334,6 +341,18 @@ macro_rules! timer_channels { let regs = unsafe { &*<$TY>::ptr() }; regs.sr.read().[< cc $index of >]().bit_is_set() } + + /// Configure the input capture input pre-filter. + /// + /// # Args + /// * `filter` - The desired input filter stage configuration. Defaults to disabled. + #[allow(dead_code)] + pub fn configure_filter(&mut self, filter: super::InputFilter) { + // 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.[< $ccmrx _input >]().modify(|_, w| w.[< ic $index f >]().bits(filter as u8)); + } } // Note(unsafe): This manually implements DMA support for input-capture channels. This diff --git a/src/lib.rs b/src/lib.rs index ab2623d..c252f37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,13 +6,3 @@ extern crate log; pub mod hardware; pub mod server; - -// 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 -pub const ADC_SAMPLE_TICKS_LOG2: u8 = 8; -pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; - -// The desired ADC sample processing buffer size. -pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; -pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2;