From bf32f0abdba668fd1cc09751fa39dd67f3eb71a4 Mon Sep 17 00:00:00 2001 From: Hubald Verzijl Date: Thu, 12 Mar 2026 17:54:20 +0100 Subject: [PATCH] Average DFT input at lower stimulation frequencies to lower noise. --- src/ad5940.rs | 18 ++++++++++++++++++ src/ad5940_registers.rs | 32 ++++++++++++++++++++++++++++++++ src/impedance.rs | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/ad5940.rs b/src/ad5940.rs index 8ba8a06..7d6eac1 100644 --- a/src/ad5940.rs +++ b/src/ad5940.rs @@ -125,6 +125,7 @@ pub struct DspConfig { muxselp: Option, ctiacon: Option, rtiacon: Option, + pub avrgnum: Option, sinc3osr: Option, sinc2osr: Option, adcsamplerate: Option, @@ -162,6 +163,11 @@ impl DspConfig { self } + pub fn avrgnum(&mut self, avrgnum: Option) -> &mut Self { + self.avrgnum = avrgnum; + self + } + pub fn sinc3osr(&mut self, sinc3osr: SINC3OSR) -> &mut Self { self.sinc3osr = Some(sinc3osr); self @@ -561,6 +567,11 @@ impl AD5940 { } } + // When averaging, the wait time needs to be multiplied by the number of averages to fill the DFT buffer with the correct number of samples + if let Some(avrgnum) = config.avrgnum { + wait_time *= 2u32.pow(avrgnum as u32 + 1); // For low frequencies, we need to average more samples to get a stable measurement + } + // Calculate wait time based on DFTNUM if let Some(dftnum) = config.dftnum { let samples_per_dft = 1 << (dftnum as u32 + 2); @@ -728,6 +739,13 @@ impl AD5940 { // ADCFILTERCON let mut current = self.read_reg(Register::ADCFILTERCON).await?; + if let Some(avrnum) = config.avrgnum { + current = AVRGEN::apply(current, AVRGEN::AverageEnabled as u32); + current = AVRGNUM::apply(current, avrnum as u32); + } else { + current = AVRGEN::apply(current, AVRGEN::AverageDisabled as u32); + } + if let Some(sinc3osr) = config.sinc3osr{ current = SINC3OSR::apply(current, sinc3osr as u32); } diff --git a/src/ad5940_registers.rs b/src/ad5940_registers.rs index 4aaac62..ee9e987 100644 --- a/src/ad5940_registers.rs +++ b/src/ad5940_registers.rs @@ -395,6 +395,23 @@ impl RegisterField for RTIACON { const MASK: u32 = 0b1111; } +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum AVRGNUM { + ADC2 = 0b00, + ADC4 = 0b01, + ADC8 = 0b10, + ADC16 = 0b11, +} + +impl RegisterField for AVRGNUM { + fn reset() -> Self { + AVRGNUM::ADC2 + } + const BIT_OFFSET: u32 = 14; + const MASK: u32 = 0b11; +} + #[allow(dead_code)] #[derive(Copy, Clone)] pub enum SINC3OSR { @@ -436,6 +453,21 @@ impl RegisterField for SINC2OSR { const MASK: u32 = 0b1111; } +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum AVRGEN { + AverageDisabled = 0b0, + AverageEnabled = 0b1, +} + +impl RegisterField for AVRGEN { + fn reset() -> Self { + AVRGEN::AverageDisabled + } + const BIT_OFFSET: u32 = 7; + const MASK: u32 = 0b1; +} + #[allow(dead_code)] #[derive(Copy, Clone)] pub enum ADCSAMPLERATE { diff --git a/src/impedance.rs b/src/impedance.rs index 98cf7f3..0ef4766 100644 --- a/src/impedance.rs +++ b/src/impedance.rs @@ -480,6 +480,11 @@ impl ImpedanceSetup { // Create vector to store the periods per DFT for each frequency let mut periods_per_dft_vec = heapless::Vec::::new(); + // Find out if we have low frequencies (<300Hz) and/or high frequencies (>=300Hz) in our measurement points for the averaging configuration + let averaging_cutoff = 300; + let mut set_avg_low_freq = number_of_points.values().iter().any(|&f| f < averaging_cutoff); + let mut set_avg_high_freq = number_of_points.values().iter().any(|&f| f >= averaging_cutoff); + // Reset FIFO self.ad5940.clear_and_enable_fifo().await.unwrap(); @@ -510,6 +515,20 @@ impl ImpedanceSetup { if let Some(dsp_config) = &mut self.dsp_config { dsp_config .dftnum(selected_dft_num); + + // Decide averaging based on frequency, apply once to avoid unnecessary writes to the registers in the loop + if frequency < averaging_cutoff && set_avg_low_freq { + dsp_config + .avrgnum(Some(AVRGNUM::ADC4)); + set_avg_low_freq = false; + self.ad5940.apply_dsp_config(dsp_config).await.unwrap(); + } + if frequency >= averaging_cutoff && set_avg_high_freq { + dsp_config + .avrgnum(None); + set_avg_high_freq = false; + self.ad5940.apply_dsp_config(dsp_config).await.unwrap(); + } // Update DFTNUM let mut current = self.ad5940.read_reg(Register::DFTCON).await.unwrap(); @@ -600,6 +619,11 @@ impl ImpedanceSetup { }, } + // Find out if we have low frequencies (<300Hz) and/or high frequencies (>=300Hz) in our measurement points for the averaging configuration + let averaging_cutoff = 300; + let mut set_avg_low_freq = number_of_points.values().iter().any(|&f| f < averaging_cutoff); + let mut set_avg_high_freq = number_of_points.values().iter().any(|&f| f >= averaging_cutoff); + // Reset FIFO self.ad5940.clear_and_enable_fifo().await.unwrap(); @@ -630,7 +654,21 @@ impl ImpedanceSetup { if let Some(dsp_config) = &mut self.dsp_config { dsp_config .dftnum(selected_dft_num); - + + // Decide averaging based on frequency, apply once to avoid unnecessary writes to the registers in the loop + if frequency < averaging_cutoff && set_avg_low_freq { + dsp_config + .avrgnum(Some(AVRGNUM::ADC4)); + set_avg_low_freq = false; + self.ad5940.apply_dsp_config(dsp_config).await.unwrap(); // Apply immediately to avoid unnecessary waits in the loop when averaging is enabled for low frequencies + } + if frequency >= averaging_cutoff && set_avg_high_freq { + dsp_config + .avrgnum(None); + set_avg_high_freq = false; + self.ad5940.apply_dsp_config(dsp_config).await.unwrap(); // Apply immediately to avoid unnecessary waits in the loop when averaging is disabled for high frequencies + } + // Update DFTNUM let mut current = self.ad5940.read_reg(Register::DFTCON).await.unwrap(); current = DFTNUM::apply(current, selected_dft_num as u32);