diff --git a/src/ad5940.rs b/src/ad5940.rs index a9a683d..8a81907 100644 --- a/src/ad5940.rs +++ b/src/ad5940.rs @@ -7,6 +7,30 @@ use heapless::LinearMap; use crate::ad5940_registers::*; +#[allow(dead_code)] +#[derive(Default)] +pub struct ClkConfig { + adcclkdiv: Option, + sysclkdiv: Option, + clk32mhzen: Option, +} + +impl ClkConfig { + pub fn adcclkdiv(mut self, adcclkdiv: ADCCLKDIV) -> Self { + self.adcclkdiv = Some(adcclkdiv); + self + } + + pub fn sysclkdiv(mut self, sysclkdiv: SYSCLKDIV) -> Self { + self.sysclkdiv = Some(sysclkdiv); + self + } + pub fn clk32mhzen(mut self, clk32mhzen: CLK32MHZEN) -> Self { + self.clk32mhzen = Some(clk32mhzen); + self + } +} + #[allow(dead_code)] #[derive(Default)] pub struct SwitchConfig { @@ -57,6 +81,9 @@ pub struct DspConfig { dftin: Option, dftnum: Option, hanning: Option, + pub fsys: Option, + pub fadc: Option, + ratio_sys2adc_clk: Option, } impl DspConfig { @@ -109,6 +136,13 @@ impl DspConfig { self.hanning = Some(hanning); self } + + pub fn set_clks(mut self, fsys: u32, fadc: u32) -> Self { + self.fsys = Some(fsys); + self.fadc = Some(fadc); + self.ratio_sys2adc_clk = Some(fsys as f32 / fadc as f32); + self + } } #[allow(dead_code)] @@ -482,9 +516,17 @@ impl AD5940 { // When ACLK = 16MHz, ADC samplerate is 800kHz // When ACLK = 32MHz, ADC samplerate is 1.6MHz - // --> Always per ADC sample 20 cycles + // If ACLK == SYSCLK --> Always per ADC sample 20 cycles + // If ACLK != SYSCLK --> Always per ADC sample 20*(SYSCLK/ACLK) cycles + // When SYSCLK is lower, the wait cycles to wait are less becasuse the ADC samples at higher rate wait_time *= 20; + if let Some(ratio) = config.ratio_sys2adc_clk { + wait_time = (wait_time as f32 * ratio) as u32; + } else { + return None; // Ratio must be set + } + Some(wait_time) } @@ -517,6 +559,35 @@ impl AD5940 { Ok(()) } + pub async fn apply_clk_config(&mut self, config: &ClkConfig) -> Result<(), Error> { + // PMBW + let mut current = self.read_reg(Register::PMBW).await?; + current |= 0b1; + self.write_reg(Register::PMBW, current).await?; + + // CLKCON0 + let mut current = self.read_reg(Register::CLKCON0).await?; + + if let Some(adcclkdiv) = config.adcclkdiv { + current = ADCCLKDIV::apply(current, adcclkdiv as u32); + } + + if let Some(sysclkdiv) = config.sysclkdiv { + current = SYSCLKDIV::apply(current, sysclkdiv as u32); + } + self.write_reg(Register::CLKCON0, current).await?; + + // HSOSCCON + let mut current = self.read_reg(Register::HSOSCCON).await?; + if let Some(clk32mhzen) = config.clk32mhzen { + current = CLK32MHZEN::apply(current, clk32mhzen as u32); + } + + self.write_reg(Register::HSOSCCON, current).await?; + + Ok(()) + } + pub async fn apply_dsp_config(&mut self, config: &DspConfig) -> Result<(), Error> { // ADCCON let mut current = self.read_reg(Register::ADCCON).await?; @@ -782,5 +853,7 @@ pub enum Register { HSRTIACON = 0x0000_20F0, // High Speed RTIA Configuration Register BUFSENCON = 0x0000_2180, // HIGH POWER AND LOW POWER BUFFER CONTROL REGISTER FIFOCNTSTA = 0x0000_2200, // Command and data FIFO internal data count register - ADCFILTERCON = 0x0000_2044 // ADC Output Filters Configuration Register + ADCFILTERCON = 0x0000_2044, // ADC Output Filters Configuration Register + CLKCON0 = 0x0000_0408, // Clock Divider Configuration Register + HSOSCCON = 0x0000_20BC, // High Power Oscillator Configuration Register } \ No newline at end of file diff --git a/src/ad5940_registers.rs b/src/ad5940_registers.rs index 7fa97a8..49ce3c2 100644 --- a/src/ad5940_registers.rs +++ b/src/ad5940_registers.rs @@ -420,6 +420,128 @@ impl RegisterField for DATAFIFOEN { const MASK: u32 = 0b1; } +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum ADCCLKDIV { + DIV1 = 0b01, + DIV2 = 0b10, +} + +impl RegisterField for ADCCLKDIV { + fn reset() -> Self { + Self::DIV1 + } + const BIT_OFFSET: u32 = 6; + const MASK: u32 = 0b1111; +} + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum SYSCLKDIV { + DIV1 = 1, + DIV2, + DIV3, + DIV4, + DIV5, + DIV6, + DIV7, + DIV8, + DIV9, + DIV10, + DIV11, + DIV12, + DIV13, + DIV14, + DIV15, + DIV16, + DIV17, + DIV18, + DIV19, + DIV20, + DIV21, + DIV22, + DIV23, + DIV24, + DIV25, + DIV26, + DIV27, + DIV28, + DIV29, + DIV30, + DIV31, +} + +impl TryFrom for SYSCLKDIV { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(SYSCLKDIV::DIV1), + 2 => Ok(SYSCLKDIV::DIV2), + 3 => Ok(SYSCLKDIV::DIV3), + 4 => Ok(SYSCLKDIV::DIV4), + 5 => Ok(SYSCLKDIV::DIV5), + 6 => Ok(SYSCLKDIV::DIV6), + 7 => Ok(SYSCLKDIV::DIV7), + 8 => Ok(SYSCLKDIV::DIV8), + 9 => Ok(SYSCLKDIV::DIV9), + 10 => Ok(SYSCLKDIV::DIV10), + 11 => Ok(SYSCLKDIV::DIV11), + 12 => Ok(SYSCLKDIV::DIV12), + 13 => Ok(SYSCLKDIV::DIV13), + 14 => Ok(SYSCLKDIV::DIV14), + 15 => Ok(SYSCLKDIV::DIV15), + 16 => Ok(SYSCLKDIV::DIV16), + 17 => Ok(SYSCLKDIV::DIV17), + 18 => Ok(SYSCLKDIV::DIV18), + 19 => Ok(SYSCLKDIV::DIV19), + 20 => Ok(SYSCLKDIV::DIV20), + 21 => Ok(SYSCLKDIV::DIV21), + 22 => Ok(SYSCLKDIV::DIV22), + 23 => Ok(SYSCLKDIV::DIV23), + 24 => Ok(SYSCLKDIV::DIV24), + 25 => Ok(SYSCLKDIV::DIV25), + 26 => Ok(SYSCLKDIV::DIV26), + 27 => Ok(SYSCLKDIV::DIV27), + 28 => Ok(SYSCLKDIV::DIV28), + 29 => Ok(SYSCLKDIV::DIV29), + 30 => Ok(SYSCLKDIV::DIV30), + 31 => Ok(SYSCLKDIV::DIV31), + _ => Err("Invalid SYSCLKDIV value"), + } + } +} + +impl SYSCLKDIV { + pub fn div(value: u8) -> Self { + SYSCLKDIV::try_from(value).unwrap_or(SYSCLKDIV::DIV1) + } +} + +impl RegisterField for SYSCLKDIV { + fn reset() -> Self { + SYSCLKDIV::DIV1 + } + + const BIT_OFFSET: u32 = 0; + const MASK: u32 = 0b1_1111; +} + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum CLK32MHZEN { + MHz32 = 0b0, + Mhz16 = 0b1, +} + +impl RegisterField for CLK32MHZEN { + fn reset() -> Self { + CLK32MHZEN::Mhz16 + } + const BIT_OFFSET: u32 = 2; + const MASK: u32 = 0b1; +} + bitflags! { // Configuration Register // Address 0x00002000, Reset: 0x00080000, Name: AFECON diff --git a/src/impedance.rs b/src/impedance.rs index 8a37544..7518533 100644 --- a/src/impedance.rs +++ b/src/impedance.rs @@ -42,18 +42,27 @@ impl ImpedanceSetup { ) .await; + // Set CLK configuration + let clk_config = ClkConfig::default() + .adcclkdiv(ADCCLKDIV::DIV1) // ADCCLK = 32MHz + .sysclkdiv(SYSCLKDIV::DIV2) // SYSCLK = 16MHz + .clk32mhzen(CLK32MHZEN::MHz32); + + self.ad5940.apply_clk_config(&clk_config).await.unwrap(); + // Set DSP configuration let dsp_config = DspConfig::default() .adc_mux_n(MUXSELN::HsTiaNeg) .adc_mux_p(MUXSELP::HsTiaPos) .ctiacon(CTIACON::C32) .rtiacon(RTIACON::R1k) - .sinc3osr(SINC3OSR::R5) + .sinc3osr(SINC3OSR::R4) .sinc2osr(SINC2OSR::R178) - .adcsamplerate(ADCSAMPLERATE::R800kHz) + .adcsamplerate(ADCSAMPLERATE::R1_6MHz) .dftin_sel(DFTINSEL::GainOffset) - .dftnum(DFTNUM::Num2048) - .hanning(true); + .dftnum(DFTNUM::Num4096) + .hanning(true) + .set_clks(16_000_000, 32_000_000); // Check clk_config: In this case SYSCLK = 16MHz and ADCCLK = 32MHz self.ad5940.apply_dsp_config(&dsp_config).await.unwrap(); self.dsp_config = Some(dsp_config); @@ -83,7 +92,7 @@ impl ImpedanceSetup { let mut wait_time = 0; if let Some(dsp_config) = &self.dsp_config { wait_time = self.ad5940.sequencer_calculate_wait_time(dsp_config).await.unwrap(); - info!("Sinus periods per DFT: {}", wait_time as f32 / 16e6 * frequency as f32); + info!("Sinus periods per DFT: {}", wait_time as f32 / dsp_config.fsys.unwrap() as f32 * frequency as f32); } else { error!("DSP configuration not set, cannot calculate wait time"); } @@ -104,10 +113,10 @@ impl ImpedanceSetup { .dmuxcon(DMUXCON::DR0Closed); self.ad5940.apply_switch_config(switch_config).await.unwrap(); self.ad5940.afecon(AFECON::WAVEGENEN | AFECON::ADCEN, true).await; - self.ad5940.sequencer_wait(16*10).await; // 10 us + self.ad5940.sequencer_wait(16*10).await; // 10 us based on SYSCLK = 16MHz self.ad5940.afecon(AFECON::ADCCONVEN | AFECON::DFTEN, true).await; self.ad5940.sequencer_wait(wait_time).await; // Determined above - self.ad5940.sequencer_wait(16*20).await; // 10 us + self.ad5940.sequencer_wait(16*20).await; // 20 us based on SYSCLK = 16MHz self.ad5940.afecon(AFECON::WAVEGENEN | AFECON:: ADCEN | AFECON::ADCCONVEN | AFECON::DFTEN, false).await; // Rz @@ -119,15 +128,15 @@ impl ImpedanceSetup { .dmuxcon(DMUXCON::D5Closed); self.ad5940.apply_switch_config(switch_config).await.unwrap(); self.ad5940.afecon(AFECON::WAVEGENEN | AFECON::ADCEN, true).await; - self.ad5940.sequencer_wait(16*10).await; // 10 us + self.ad5940.sequencer_wait(16*10).await; // 10 us based on SYSCLK = 16MHz self.ad5940.afecon(AFECON::ADCCONVEN | AFECON::DFTEN, true).await; self.ad5940.sequencer_wait(wait_time).await; // Determined above - self.ad5940.sequencer_wait(16*20).await; // 10 us + self.ad5940.sequencer_wait(16*20).await; // 20 us based on SYSCLK = 16MHz self.ad5940.afecon(AFECON::WAVEGENEN | AFECON:: ADCEN | AFECON::ADCCONVEN | AFECON::DFTEN, false).await; // Toggle leds self.ad5940.write_reg(Register::SYNCEXTDEVICE, 0b010).await.unwrap(); - self.ad5940.sequencer_wait(16 * 1_000).await; // 0.025 second + self.ad5940.sequencer_wait(16 * 1_000).await; // 1ms based on SYSCLK = 16MHz self.ad5940.write_reg(Register::SYNCEXTDEVICE, 0b111).await.unwrap(); self.ad5940.sequencer_enable(false).await;