From f1015ee00d383a667b7c506056774e246080d62b Mon Sep 17 00:00:00 2001 From: Hubald Verzijl Date: Tue, 12 Aug 2025 22:44:08 +0200 Subject: [PATCH] Added external interrupt and test to determine impedance. Not working yet. --- Cargo.lock | 1 - Cargo.toml | 2 +- src/ad5940.rs | 38 +++++++++-- src/ad5940_registers.rs | 7 ++ src/main.rs | 137 +++++++++++++++++++++++++++++++++++----- 5 files changed, 162 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee4632d..a251f9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,7 +409,6 @@ dependencies = [ "defmt 0.3.100", "document-features", "embassy-embedded-hal 0.3.1", - "embassy-executor", "embassy-futures", "embassy-hal-internal 0.2.0", "embassy-net-driver", diff --git a/Cargo.toml b/Cargo.toml index 5b39d03..3961e1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # Change stm32h563zi to your chip name, if necessary. -embassy-stm32 = { version = "0.2.0", features = ["defmt", "stm32h533re", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } +embassy-stm32 = { version = "0.2.0", features = ["defmt", "stm32h533re", "memory-x", "time-driver-any", "exti", "unstable-pac"] } embassy-sync = { version = "0.6.0", features = ["defmt"] } embassy-executor = { version = "0.7.0", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/src/ad5940.rs b/src/ad5940.rs index 7fa46e3..9c9e3cc 100644 --- a/src/ad5940.rs +++ b/src/ad5940.rs @@ -207,8 +207,8 @@ impl AD5940 { // info!("DATAFIFORD: 0x{:08X}", test); // } - let test = self.read_reg_raw(0x2200).await.unwrap(); - info!("FIFOCNTSTA: {}", (test>>16) & 0b111_1111_1111); + // let test = self.read_reg_raw(0x2200).await.unwrap(); + // info!("FIFOCNTSTA: {}", (test>>16) & 0b111_1111_1111); } pub async fn afecon(&mut self, ctr: AFECON, state: bool) { @@ -249,6 +249,22 @@ impl AD5940 { let reg = self.read_reg(Register::SWCON).await.unwrap(); let mut reg = SWCON::from_bits_truncate(reg); + // T9CON + if ctr.contains(SWCON::T9CON) { + reg |= SWCON::T9CON; + } else { + reg &= !SWCON::T9CON; + } + + // TMUXCON + if (ctr & SWCON::TMUXCON_MSK) == SWCON::TMUXCON_T2 { + reg &= !SWCON::TMUXCON_MSK; + reg |= SWCON::TMUXCON_T2; + } else if (ctr & SWCON::TMUXCON_MSK) == SWCON::TMUXCON_TR1 { + reg &= !SWCON::TMUXCON_MSK; + reg |= SWCON::TMUXCON_TR1; + } + // NMUXCON if (ctr & SWCON::NMUXCON_MSK) == SWCON::NMUXCON_N2 { reg &= !SWCON::NMUXCON_MSK; @@ -256,7 +272,10 @@ impl AD5940 { } else if (ctr & SWCON::NMUXCON_MSK) == SWCON::NMUXCON_N5 { reg &= !SWCON::NMUXCON_MSK; reg |= SWCON::NMUXCON_N5; - }; + } else if (ctr & SWCON::NMUXCON_MSK) == SWCON::NMUXCON_NR1 { + reg &= !SWCON::NMUXCON_MSK; + reg |= SWCON::NMUXCON_NR1; + } // PMUXCON if (ctr & SWCON::PMUXCON_MSK) == SWCON::PMUXCON_P2 { @@ -265,12 +284,18 @@ impl AD5940 { } else if (ctr & SWCON::PMUXCON_MSK) == SWCON::PMUXCON_P11 { reg &= !SWCON::PMUXCON_MSK; reg |= SWCON::PMUXCON_P11; + } else if (ctr & SWCON::PMUXCON_MSK) == SWCON::PMUXCON_PR0 { + reg &= !SWCON::PMUXCON_MSK; + reg |= SWCON::PMUXCON_PR0; } // DMUXCON - if ctr.contains(SWCON::DMUXCON_D5) { + if (ctr & SWCON::DMUXCON_MSK) == SWCON::DMUXCON_D5 { reg &= !SWCON::DMUXCON_MSK; reg |= SWCON::DMUXCON_D5; + } else if { ctr & SWCON::DMUXCON_MSK } == SWCON::DMUXCON_DR0 { + reg &= !SWCON::DMUXCON_MSK; + reg |= SWCON::DMUXCON_DR0; } self.write_reg(Register::SWCON, reg.bits()).await.unwrap(); @@ -397,7 +422,7 @@ impl AD5940 { | SWCON::PMUXCON_P11 | SWCON::DMUXCON_D5).await; // - self.write_reg_raw(0x0000_20D0, 0b1000 << 4).await.unwrap(); + self.write_reg_raw(0x0000_20D0, 0b1000 << 4).await.unwrap(); // 1024 OSR // SINC3 = 5 --> 160000 Hz // SINC2 = 178 --> 898,8764044944Hz // ... (DFTNUM = 2048) @@ -452,4 +477,7 @@ pub enum Register { SEQTIMEOUT = 0x0000_2068, // Sequencer Timeout Counter Register SEQCRC = 0x0000_2060, // Sequencer CRC Value Register DATAFIFOTHRES = 0x0000_21E0, // Data FIFO Threshold Register + SYNCEXTDEVICE = 0x0000_2054, // Sync External Device Register + GP0CON = 0x0000_0000, // GPIO Port 0 Configuration Register + DATAFIFORD = 0x0000_206C, // Data FIFO Read Register } diff --git a/src/ad5940_registers.rs b/src/ad5940_registers.rs index a9fe431..989ec8c 100644 --- a/src/ad5940_registers.rs +++ b/src/ad5940_registers.rs @@ -28,13 +28,20 @@ bitflags! { #[derive(Clone, Copy)] #[derive(PartialEq)] pub struct SWCON: u32 { + const T9CON = 1 << 17; // T9 switch // RTIA switch + const TMUXCON_MSK = 0b1111 << 12; + const TMUXCON_T2 = 0b0010 << 12; // T2 switch + const TMUXCON_TR1 = 0b1000 << 12; // TR1 switch const NMUXCON_MSK = 0b1111 << 8; const NMUXCON_N2 = 0b0010 << 8; // N2 switch const NMUXCON_N5 = 0b0101 << 8; // N5 switch + const NMUXCON_NR1 = 0b1010 << 8; // NR1 switch const PMUXCON_MSK = 0b1111 << 4; + const PMUXCON_PR0 = 0b0001 << 4; // PR0 switch const PMUXCON_P2 = 0b0010 << 4; // P2 switch const PMUXCON_P11 = 0b1011 << 4; // P11 switch const DMUXCON_MSK = 0b1111; + const DMUXCON_DR0 = 0b0001; // DR0 switch const DMUXCON_D5 = 0b0101; // D5 switch } } diff --git a/src/main.rs b/src/main.rs index 1f2414e..7b17502 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,14 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; use embassy_time::{Timer, Duration}; use embassy_futures::{select::select, select::Either}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::{i2c, spi, Config}; use embassy_stm32::time::Hertz; -use crate::ad5940_registers::{AFECON, AFEGENINTSTA}; +use crate::ad5940_registers::{AFECON, SWCON}; use {defmt_rtt as _, panic_probe as _}; @@ -69,6 +70,10 @@ async fn main(spawner: Spawner) { let cs = Output::new(p.PC9, Level::High, Speed::Low); let rst = Output::new(p.PB3, Level::High, Speed::Low); + // Set up interrupt at GPIO for AD5940 + let ad5940_gpio_0 = ExtiInput::new(p.PC8, p.EXTI8, embassy_stm32::gpio::Pull::Up); + spawner.must_spawn(ad5940_readout_task(ad5940_gpio_0)); + let spi = spi::Spi::new_blocking( p.SPI1, p.PA5, // SCK @@ -103,18 +108,37 @@ async fn main(spawner: Spawner) { // // electrodes.set(Electrode::E12, AD5940Pin::RE0, State::ENABLED); // Turn on the green LED - ad5940.write_reg_raw(0x0000_0004, 1 << 1).await.unwrap(); - ad5940.write_reg_raw(0x0000_001C, 1 << 1).await.unwrap(); + // ad5940.write_reg_raw(0x0000_0004, 1 << 1).await.unwrap(); + // ad5940.write_reg_raw(0x0000_001C, 1 << 1).await.unwrap(); - // // Sequencer test + ad5940.write_reg(ad5940::Register::GP0CON, 0b10 << 4 | 0b10 << 2 | 0b10).await.unwrap(); + ad5940.write_reg(ad5940::Register::SYNCEXTDEVICE, 0b111).await.unwrap(); + + // Sequencer test ad5940.sequencer_enable(true).await; - ad5940.afecon(AFECON::WAVEGENEN, true).await; - ad5940.wgfcw(1000).await; - ad5940.sequencer_wait(300_000).await; - ad5940.wgfcw(2000).await; - ad5940.sequencer_wait(160_000).await; - // ad5940.sequencer_trigger_interrupt(AFEGENINTSTA::CUSTOMINT0).await; - ad5940.afecon(AFECON::WAVEGENEN, false).await; + ad5940.wgfcw(100).await; + + // Rcal + ad5940.swcon(SWCON::DMUXCON_DR0 | SWCON::PMUXCON_PR0 | SWCON::NMUXCON_NR1 | SWCON::TMUXCON_TR1 | SWCON::T9CON).await; // RCAL0 --> + ad5940.afecon(AFECON::WAVEGENEN | AFECON::ADCEN, true).await; + ad5940.sequencer_wait(16*10).await; // 10 us + ad5940.afecon(AFECON::ADCCONVEN | AFECON::DFTEN, true).await; + ad5940.sequencer_wait(16 * 1_500_000).await; // 1.5 second + ad5940.afecon(AFECON::WAVEGENEN | AFECON:: ADCEN | AFECON::ADCCONVEN | AFECON::DFTEN, false).await; + + // Rz + ad5940.swcon(SWCON::DMUXCON_D5 | SWCON::PMUXCON_P11 | SWCON::NMUXCON_N2 | SWCON::TMUXCON_MSK | SWCON::TMUXCON_T2).await; + ad5940.afecon(AFECON::WAVEGENEN | AFECON::ADCEN, true).await; + ad5940.sequencer_wait(16*10).await; // 10 us + ad5940.afecon(AFECON::ADCCONVEN | AFECON::DFTEN, true).await; + ad5940.sequencer_wait(16 * 1_500_000).await; // 1.5 second + ad5940.afecon(AFECON::WAVEGENEN | AFECON:: ADCEN | AFECON::ADCCONVEN | AFECON::DFTEN, false).await; + + // Toggle leds + ad5940.write_reg(ad5940::Register::SYNCEXTDEVICE, 0b010).await.unwrap(); + ad5940.sequencer_wait(16 * 100_000).await; // 0.1 second + ad5940.write_reg(ad5940::Register::SYNCEXTDEVICE, 0b111).await.unwrap(); + ad5940.sequencer_enable(false).await; // // Configure the sequencer cmd data sram @@ -148,13 +172,26 @@ async fn main(spawner: Spawner) { ad5940.sequencer_trigger(0).await; - - // info!("Mainloop still running!"); - Timer::after_millis(250).await; + Timer::after_millis(3500).await; - + // let test = ad5940.read_reg_raw(0x2200).await.unwrap(); + // info!("FIFOCNTSTA: {}", (test>>16) & 0b111_1111_1111); + + let mut data: [u32; 4] = [0; 4]; + data[0] = ad5940.read_reg(ad5940::Register::DATAFIFORD).await.unwrap(); + data[1] = ad5940.read_reg(ad5940::Register::DATAFIFORD).await.unwrap(); + data[2] = ad5940.read_reg(ad5940::Register::DATAFIFORD).await.unwrap(); + data[3] = ad5940.read_reg(ad5940::Register::DATAFIFORD).await.unwrap(); + + let result = calculate_impedance(data); + + // You’ll need to implement your own logging or send this over serial in embedded + info!("Impedance: Magnitude = {} Ω, Phase = {} rad", result.magnitude, result.phase); + + // let test = ad5940.read_reg_raw(0x2200).await.unwrap(); + // info!("FIFOCNTSTA: {}", (test>>16) & 0b111_1111_1111); } } @@ -179,4 +216,72 @@ async fn green_led(mut led: Output<'static>) { led.toggle(); } -} \ No newline at end of file +} + +#[embassy_executor::task] +async fn ad5940_readout_task(mut pin: ExtiInput<'static>) { + loop { + pin.wait_for_falling_edge().await; + info!("AD5940 interrupt triggered!"); + } +} + + +extern crate libm; + +#[derive(Debug, Clone, Copy)] +pub struct Complex { + real: i32, + imag: i32, +} + +#[derive(Debug)] +pub struct ImpedanceResult { + pub magnitude: f32, + pub phase: f32, +} + +// Example Rcal value (Ohms) +const RCAL_VAL: f32 = 1000.0; + +/// Convert raw 18-bit 2's complement value to signed i32 +fn sign_extend_18bit(val: u32) -> i32 { + let masked = val & 0x3FFFF; + if masked & (1 << 17) != 0 { + (masked | 0xFFFC0000) as i32 + } else { + masked as i32 + } +} + +/// Calculate magnitude and phase of Rz using Rcal reference +pub fn calculate_impedance(data: [u32; 4]) -> ImpedanceResult { + let mut signed_data = [0i32; 4]; + for (i, &val) in data.iter().enumerate() { + signed_data[i] = sign_extend_18bit(val); + } + + let dft_rcal = Complex { + real: signed_data[0], + imag: signed_data[1], + }; + + let dft_rz = Complex { + real: signed_data[2], + imag: signed_data[3], + }; + + let rcal_mag = libm::sqrtf((dft_rcal.real as f32) * (dft_rcal.real as f32) + + (dft_rcal.imag as f32) * (dft_rcal.imag as f32)); + + let rz_mag = libm::sqrtf((dft_rz.real as f32) * (dft_rz.real as f32) + + (dft_rz.imag as f32) * (dft_rz.imag as f32)); + + let rcal_phase = libm::atan2f(-(dft_rcal.imag as f32), dft_rcal.real as f32); + let rz_phase = libm::atan2f(-(dft_rz.imag as f32), dft_rz.real as f32); + + let magnitude = (rcal_mag / rz_mag) * RCAL_VAL; + let phase = rcal_phase - rz_phase; + + ImpedanceResult { magnitude, phase } +}