Index: AD5941_interface09302025/ad5941_library_extension.cpp =================================================================== diff -u -r00e9a3fe8b32dd4867784af1134f7cc2904d579f -r449f6936bd3fd84d8c4da61448702ca24015d778 --- AD5941_interface09302025/ad5941_library_extension.cpp (.../ad5941_library_extension.cpp) (revision 00e9a3fe8b32dd4867784af1134f7cc2904d579f) +++ AD5941_interface09302025/ad5941_library_extension.cpp (.../ad5941_library_extension.cpp) (revision 449f6936bd3fd84d8c4da61448702ca24015d778) @@ -2,20 +2,20 @@ * @file ad5941_library_extension.cpp * @brief For connecting teensy microcontroller to ad5941 via SPI with interrupt functionality * @author MK, Aly Development - * @date 09/30/2025, last modified - * + * @date 09/30/2025, last modified + * * @details * Teensy 4.0 extension for the AD5941/AD5940 AFE (Analog Front End) library - * from Analog Devices Inc. + * from Analog Devices Inc. * * Provides hardware interface functions and * application-specific measurement routines for conductivity * and RTD temperature sensing. - * + * * **** Note: Any application of this software should be thoroughly tested and validated **** * * Adapted and expanded from: https://github.com/analogdevicesinc/ad5940-examples - * Note impedance.c modified for jitter + * Note impedance.c modified for jitter */ // Include guard @@ -24,8 +24,10 @@ // Include dependencies #include "ad5941_library_extension.h" +#include "DDTeensyDefs.h" -extern "C" { +extern "C" +{ #include "ad5940.h" #include "impedance.h" } @@ -34,33 +36,36 @@ uint32_t AppBuff[APPBUFF_SIZE]; // Arrays to store up to 5 measurements -float magnitudeArray[repeatNumber]; // Array to hold magnitude values (in Ohms) -float phaseArray[repeatNumber]; // Array to hold phase values (in degrees) -float storedFrequency; // Variable to store the measurement frequency +float magnitudeArray[repeatNumber]; // Array to hold magnitude values (in Ohms) +float phaseArray[repeatNumber]; // Array to hold phase values (in degrees) +float storedFrequency; // Variable to store the measurement frequency volatile static uint32_t ucInterrupted = 0; /* Flag to indicate interrupt occurred */ int currentTIA = DEFAULT_RTIA; int verboseMode = 0; -unsigned long previousMillis = 0; // Store last measurement time -const long jitterTimeout = 1500; // 1.5 seconds in milliseconds if it's been longer than this many mSec since last measurement, we assume discard first {numSettlingMeasurements} measurement as it may have startup jitter. +unsigned long previousMillis = 0; // Store last measurement time +const long jitterTimeout = 1500; // 1.5 seconds in milliseconds if it's been longer than this many mSec since last measurement, we assume discard first {numSettlingMeasurements} measurement as it may have startup jitter. - // SPI Settings for Teensy 4.0 static const SPISettings SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0); +static bool sendSensorData; + // Configuration storage structure -struct SavedConfig { +struct SavedConfig +{ float SinFreq; float DacVoltPP; float BiasVolt; uint32_t HstiaRtiaSel; uint32_t AdcPgaGain; uint32_t DftNum; uint32_t ADCAvgNum; - struct { + struct + { BoolFlag SweepEn; float SweepStart; float SweepStop; @@ -69,54 +74,83 @@ } SweepCfg; }; +// Enum to track unit status +enum UnitStatus +{ + STATUS_SKIPPED = 0, // Unit was never attempted to be initialized + STATUS_FAILED = 1, // Unit was attempted but failed initialization + STATUS_SUCCESS = 2 // Unit was successfully initialized +}; + +// Array to keep track of unit status (indexed 0-5 for units 1-6) +// Initialize all to skipped by default +UnitStatus unitStatus[6] = { + STATUS_SKIPPED, STATUS_SKIPPED, STATUS_SKIPPED, + STATUS_SKIPPED, STATUS_SKIPPED, STATUS_SKIPPED}; + +static bool isInitialized; // Set to true if the initialization function has been completely executed atleast once. +static Init_Status initStatus; // Stores the phase of initialization +static sensorDataPacketStruct sensorPacket[MAX_NUM_OF_SENSORS]; // Stores Impedance and RTD measurements for all the sensors. +static sensorDataPacketStruct singleSensorPacket; // Stores Impedance and RTD measurements for only singularly selected sensor. +static eepromDataPacketStruct eepromDataPacket; // Stores all the eeprom data +static measurementSettingsStruct measurementSettingsPacket; // Stores all the conductivity measurement settings + // Global variable to store the last known good configuration SavedConfig lastConfig; int propagateSettingsChanges = 0; -int TEENSY_RESET_PIN = 17; // Reset pin for AD5940 -int AD5940_INT_PIN = 18; // Interrupt pin from AD5940 +int TEENSY_RESET_PIN = 17; // Reset pin for AD5940 +int AD5940_INT_PIN = 18; // Interrupt pin from AD5940 float rtdVoltageValue = DEFAULT_RTD_VALUE; int outputNow = 0; /****************************************************************************** * @brief Method to deselect the chip select function of the AD5940 *****************************************************************************/ -void AD5940_CsSet(void) { +void AD5940_CsSet(void) +{ digitalWrite(TEENSY_SPI_CS_PIN, HIGH); - delayMicroseconds(10); // this is necessary to keep things from switching too fast -MK + delayMicroseconds(10); // this is necessary to keep things from switching too fast -MK } /****************************************************************************** * @brief Method to select the chip select function of the AD5940 *****************************************************************************/ -void AD5940_CsClr(void) { +void AD5940_CsClr(void) +{ digitalWrite(TEENSY_SPI_CS_PIN, LOW); - delayMicroseconds(10); // this is necessary to keep things from switching too fast -MK + delayMicroseconds(10); // this is necessary to keep things from switching too fast -MK } /****************************************************************************** * @brief Method to reset the AD5940 *****************************************************************************/ -void AD5940_RstSet(void) { +void AD5940_RstSet(void) +{ digitalWrite(TEENSY_RESET_PIN, HIGH); } /****************************************************************************** * @brief Method to clear the reset on the AD5940 *****************************************************************************/ -void AD5940_RstClr(void) { +void AD5940_RstClr(void) +{ digitalWrite(TEENSY_RESET_PIN, LOW); } /****************************************************************************** * @brief Method for delaying the AD5940 for a multiple time of 10 micro seconds - * + * * @param time: multiplier which defines the amount of times the AD5940 should * be delayed by 10 micro seconds *****************************************************************************/ -void AD5940_Delay10us(uint32_t iTime) { - if (iTime < 1638) { +void AD5940_Delay10us(uint32_t iTime) +{ + if (iTime < 1638) + { delayMicroseconds(iTime * 10); - } else { + } + else + { uint32_t iTimeDelayMicro = iTime % 1000; uint32_t iTimeDelay = iTime - iTimeDelayMicro; delay(iTimeDelay / 100); @@ -129,31 +163,33 @@ * @details: Modified for Teensy 4.0's SPI implementation * System clock frequency for the AD5940 remains at 16 MHz. * System clock frequency for Teensy 4.0 is 600 MHz. - * + * * @param pSendBuffer: Send buffer holding all data that should be sent via SPI * @param pRecvBuff: Receive buffer storing all data transmitted by the AD5940 * @param length: Length of transmitted data *****************************************************************************/ void AD5940_ReadWriteNBytes(unsigned char *pSendBuffer, unsigned char *pRecvBuff, - unsigned long length) { + unsigned long length) +{ SPI.beginTransaction(SPISettings); - for (unsigned long i = 0; i < length; i++) { + for (unsigned long i = 0; i < length; i++) + { *pRecvBuff++ = SPI.transfer(*pSendBuffer++); - delayMicroseconds(10); // Add small delay between bytes + delayMicroseconds(10); // Add small delay between bytes } SPI.endTransaction(); - delayMicroseconds(10); // Add delay after transaction + delayMicroseconds(10); // Add delay after transaction } - /****************************************************************************** * @brief Method to get the MCU interrupt flag status * @return uint32_t Returns the current state of the interrupt flag *****************************************************************************/ -uint32_t AD5940_GetMCUIntFlag(void) { +uint32_t AD5940_GetMCUIntFlag(void) +{ return ucInterrupted; } @@ -163,7 +199,8 @@ * interrupt flags on the Teensy 4.0 * @return uint32_t Returns 1 after successfully clearing the flag *****************************************************************************/ -uint32_t AD5940_ClrMCUIntFlag(void) { +uint32_t AD5940_ClrMCUIntFlag(void) +{ ucInterrupted = 0; #ifdef triggDiag Serial.println("clear trigger"); @@ -172,19 +209,20 @@ return 1; } - /****************************************************************************** * @brief error reporting function; only if ADI_DEBUG enabled in ad5940.h` * @details note must update in ad5940.h to set debug method output - * see: - * #define ADI_DEBUG + * see: + * #define ADI_DEBUG * #ifdef ADI_DEBUG * #define ADI_Print canary * #endif *****************************************************************************/ -void canary(const char *format, ...) { - if (verboseMode) { // only print in verbose mode - char buffer[256]; // Buffer for formatted string +void canary(const char *format, ...) +{ + if (verboseMode) + { // only print in verbose mode + char buffer[256]; // Buffer for formatted string va_list args; va_start(args, format); @@ -205,48 +243,50 @@ /****************************************************************************** * @brief simple interupt handler *****************************************************************************/ -void AD5940_InterruptHandler() { +void AD5940_InterruptHandler() +{ ucInterrupted = 1; #ifdef triggDiag Serial.println("triggered"); #endif } - - /****************************************************************************** * APPLICATION SPECIFIC FUNCTIONS - * + * * The following functions extend the base AD5940 library with application- * specific implementations for impedance measurement, RTD sensing, and * system configuration management. *****************************************************************************/ /** * @brief convert mV to WgCode for setting on HSDAC - * - * Double check this function before any critical deployment. It was tested and matched emperically for 25mV, but should be verified for more complex usages as needed. - * + * + * Double check this function before any critical deployment. It was tested and matched emperically for 25mV, but should be verified for more complex usages as needed. + * * @return uint16_t - WgCode for HSDAC */ -uint16_t mV_to_WgCode(float mV) // +uint16_t mV_to_WgCode(float mV) // { - const float VDAC_MIN = 0.0f; // 0 V - const float VDAC_MAX = 1.82f; // full-scale HSDAC + const float VDAC_MIN = 0.0f; // 0 V + const float VDAC_MAX = 1.82f; // full-scale HSDAC const float VBIAS = 0; - float Vs = mV * 1e-3f; // V - float Vdac = (Vs + VBIAS) * 0.5f; // V + float Vs = mV * 1e-3f; // V + float Vdac = (Vs + VBIAS) * 0.5f; // V - if (Vdac < VDAC_MIN) Vdac = VDAC_MIN; - if (Vdac > VDAC_MAX) Vdac = VDAC_MAX; + if (Vdac < VDAC_MIN) + Vdac = VDAC_MIN; + if (Vdac > VDAC_MAX) + Vdac = VDAC_MAX; return (uint16_t)(Vdac * 4095.0f / 1.82f + 0.5f); } -uint16_t mV_to_WgCodeUpdate(float mV) { - const float VDAC_MIN = 0.0f; // 0 V - const float VDAC_MAX = 1.82f; // full-scale HSDAC (1.82V reference) +uint16_t mV_to_WgCodeUpdate(float mV) +{ + const float VDAC_MIN = 0.0f; // 0 V + const float VDAC_MAX = 1.82f; // full-scale HSDAC (1.82V reference) // Convert mV to volts float Vs = mV * 1e-3f; @@ -257,51 +297,57 @@ // Handle negative voltages by using the bipolar capability // The AD5940 can do bipolar output around a bias point - if (Vs < 0) { + if (Vs < 0) + { // For negative voltages, we might need to use a different approach // depending on your circuit configuration - Vdac = 0.91f + Vs; // 0.91V is mid-scale for 1.82V reference - } else { - Vdac = 0.91f + Vs; // Positive voltages above mid-scale + Vdac = 0.91f + Vs; // 0.91V is mid-scale for 1.82V reference } + else + { + Vdac = 0.91f + Vs; // Positive voltages above mid-scale + } // Clamp to valid DAC range - if (Vdac < VDAC_MIN) Vdac = VDAC_MIN; - if (Vdac > VDAC_MAX) Vdac = VDAC_MAX; + if (Vdac < VDAC_MIN) + Vdac = VDAC_MIN; + if (Vdac > VDAC_MAX) + Vdac = VDAC_MAX; // Convert to 12-bit DAC code (0-4095) uint16_t code = (uint16_t)(Vdac * 4095.0f / 1.82f + 0.5f); return code; } - /** - * @brief sample from ADC with delay - * - * Double check this function before any critical deployment. It was tested and matched emperically for 25mV, but should be verified for more complex usages as needed. - * + * @brief sample from ADC with delay + * + * Double check this function before any critical deployment. It was tested and matched emperically for 25mV, but should be verified for more complex usages as needed. + * * @return uint32_t code, result code from ADC */ -static inline uint32_t sampleADC(uint32_t t10us) { +static inline uint32_t sampleADC(uint32_t t10us) +{ AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE); AD5940_Delay10us(t10us); uint32_t code = AD5940_ReadAfeResult(AFERESULT_SINC2); AD5940_AFECtrlS(AFECTRL_ADCCNV, bFALSE); - //Serial.println(code); + // Serial.println(code); return code; } /** * @brief Improved RTD measurement function with better error handling and stability - * + * * Key improvements: * - Better settling time management * - More robust ADC sampling * - Improved error handling * - Consistent switch matrix configuration */ -float AppRTDMeasure(float sensor_mV) { +float AppRTDMeasure(float sensor_mV) +{ AFERefCfg_Type aferef_cfg; HSLoopCfg_Type HsLoopCfg; DSPCfg_Type dsp_cfg; @@ -312,13 +358,14 @@ const float RtdRefRes = REF_RESISTOR_VALUE; // Wake up the AD5940 from low power mode - if (AD5940_WakeUp(10) > 10) { - Serial.println("ERROR: AD5940 wakeup timeout"); + if (AD5940_WakeUp(10) > 10) + { + // Serial.println("ERROR: AD5940 wakeup timeout"); return -1000.0f; } // Reset LPDAC configuration before RTD measurement - LPDACCfg_Type lpdac_reset = { 0 }; + LPDACCfg_Type lpdac_reset = {0}; lpdac_reset.PowerEn = bFALSE; AD5940_LPDACCfgS(&lpdac_reset); // no longer change ref subsystem between RTD and conductivity measurements -MK @@ -388,19 +435,20 @@ // Restart waveform generator with proper sequence AD5940_AFECtrlS(AFECTRL_WG, bFALSE); - AD5940_Delay10us(1000); // 10ms settling + AD5940_Delay10us(1000); // 10ms settling AD5940_AFECtrlS(AFECTRL_WG, bTRUE); // Extended settling time for RTD measurement - AD5940_Delay10us(500); // 5ms settling time + AD5940_Delay10us(500); // 5ms settling time // Sample RTD with improved averaging sampleADC(1); uint64_t accRTD = 0; - for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) { - accRTD += sampleADC(1); // Increased sampling delay + for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) + { + accRTD += sampleADC(1); // Increased sampling delay // Serial.println(accRTD); - AD5940_Delay10us(10); // Small delay between samples + AD5940_Delay10us(10); // Small delay between samples } adcCode_rtd = accRTD / NUM_RTD_SAMPLES; @@ -414,11 +462,12 @@ AD5940_SWMatrixCfgS(&HsLoopCfg.SWMatCfg); // Additional settling time after switch change - AD5940_Delay10us(500); // 5ms settling + AD5940_Delay10us(500); // 5ms settling sampleADC(1); // Sample reference resistor uint64_t accREF = 0; - for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) { + for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) + { accREF += sampleADC(1); AD5940_Delay10us(10); } @@ -446,7 +495,8 @@ rtd_resistance = RtdRefRes * (volt_ref / volt_rtd); // Verbose output - if (verboseMode) { //shrinking this output to avoid being too long + if (verboseMode) + { // shrinking this output to avoid being too long // Serial.println(""); // Serial.println("verboseMode output"); // Serial.print("sensor_mV"); @@ -461,16 +511,35 @@ Serial.printf(" NUM_SAMPLES: %u", NUM_RTD_SAMPLES); Serial.println(" "); Serial.print(" RTD TIA Resistor: "); - switch (RTD_RTIA) { - case HSTIARTIA_200: Serial.println("200 Ohm"); break; - case HSTIARTIA_1K: Serial.println("1K Ohm"); break; - case HSTIARTIA_5K: Serial.println("5K Ohm"); break; - case HSTIARTIA_10K: Serial.println("10K Ohm"); break; - case HSTIARTIA_20K: Serial.println("20K Ohm"); break; - case HSTIARTIA_40K: Serial.println("40K Ohm"); break; - case HSTIARTIA_80K: Serial.println("80K Ohm"); break; - case HSTIARTIA_160K: Serial.println("160K Ohm"); break; - default: Serial.println("Unknown"); break; + switch (RTD_RTIA) + { + case HSTIARTIA_200: + Serial.println("200 Ohm"); + break; + case HSTIARTIA_1K: + Serial.println("1K Ohm"); + break; + case HSTIARTIA_5K: + Serial.println("5K Ohm"); + break; + case HSTIARTIA_10K: + Serial.println("10K Ohm"); + break; + case HSTIARTIA_20K: + Serial.println("20K Ohm"); + break; + case HSTIARTIA_40K: + Serial.println("40K Ohm"); + break; + case HSTIARTIA_80K: + Serial.println("80K Ohm"); + break; + case HSTIARTIA_160K: + Serial.println("160K Ohm"); + break; + default: + Serial.println("Unknown"); + break; } // Serial.print("volt_ref: "); // Serial.println(volt_ref, 6); // More precision @@ -492,28 +561,30 @@ /** * @brief Resets the AD5940 switch matrix to its default state - * + * * This function creates a clean configuration for the AD5940 switch matrix * by clearing all switch selections. It ensures that all internal connections * are opened before setting up a new configuration, preventing potential * conflicts or shorts between different signal paths. */ -void resetSwitchMatrix() { +void resetSwitchMatrix() +{ // Create a switch matrix configuration structure SWMatrixCfg_Type sw_cfg; // Clear all switch selections by setting them to 0 - sw_cfg.Dswitch = 0; // D switches (typically connected to excitation source) - sw_cfg.Pswitch = 0; // P switches (typically connected to positive input) - sw_cfg.Nswitch = 0; // N switches (typically connected to negative input) - sw_cfg.Tswitch = 0; // T switches (typically connected to TIA feedback network) + sw_cfg.Dswitch = 0; // D switches (typically connected to excitation source) + sw_cfg.Pswitch = 0; // P switches (typically connected to positive input) + sw_cfg.Nswitch = 0; // N switches (typically connected to negative input) + sw_cfg.Tswitch = 0; // T switches (typically connected to TIA feedback network) // Apply the configuration to the AD5940 hardware AD5940_SWMatrixCfgS(&sw_cfg); } // Function to save current configuration -void saveCurrentConfig() { +void saveCurrentConfig() +{ AppIMPCfg_Type *pImpedanceCfg; AppIMPGetCfg(&pImpedanceCfg); lastConfig.SinFreq = pImpedanceCfg->SinFreq; @@ -531,7 +602,8 @@ } // Function to recall saved configuration -void recallSavedConfig() { +void recallSavedConfig() +{ AppIMPCfg_Type *pImpedanceCfg; AppIMPGetCfg(&pImpedanceCfg); pImpedanceCfg->SinFreq = lastConfig.SinFreq; @@ -551,16 +623,17 @@ /** * @brief Handles configuration commands sent via serial interface - * + * * This function processes configuration commands in CSV format: * cfg,parameter,value - * - * It allows users to modify impedance measurement parameters, + * + * It allows users to modify impedance measurement parameters, * save configurations, and recall saved configurations. - * + * * @param cmd String containing the configuration command */ -void handleConfigCommand(String cmd) { +void handleConfigCommand(String cmd) +{ // Command format: cfg,parameter,value // Examples: // - cfg,sinfreq,1000.0 (Set sine wave frequency to 1000 Hz) @@ -571,19 +644,23 @@ int startIndex = 0; // Parse command string using comma delimiter - for (uint i = 0; i < cmd.length(); i++) { - if (cmd.charAt(i) == ',') { + for (uint i = 0; i < cmd.length(); i++) + { + if (cmd.charAt(i) == ',') + { params[paramIndex] = cmd.substring(startIndex, i); startIndex = i + 1; paramIndex++; // Safety check to prevent array overflow - if (paramIndex >= 3) break; + if (paramIndex >= 3) + break; } } // Get last parameter (after the last comma) - if (paramIndex < 3) { + if (paramIndex < 3) + { params[paramIndex] = cmd.substring(startIndex); } @@ -596,113 +673,163 @@ String value = params[2]; // Handle special commands for saving/recalling configurations - if (param == "save") { + if (param == "save") + { saveCurrentConfig(); Serial.println("Configuration saved"); return; - } else if (param == "recall") { + } + else if (param == "recall") + { recallSavedConfig(); Serial.println("Configuration recalled"); return; } // Process different configuration parameters - if (param == "sinfreq") { + if (param == "sinfreq") + { // Set sine wave frequency (Hz) pImpedanceCfg->SinFreq = value.toFloat(); Serial.printf("Set SinFreq to: %.2f Hz\n", pImpedanceCfg->SinFreq); - } else if (param == "dacpp") { + } + else if (param == "dacpp") + { // Set DAC peak-to-peak voltage (mV) pImpedanceCfg->DacVoltPP = value.toFloat(); Serial.printf("Set DacVoltPP to: %.2f mV\n", pImpedanceCfg->DacVoltPP); - } else if (param == "bias") { + } + else if (param == "bias") + { // Set DC bias voltage (mV) pImpedanceCfg->BiasVolt = value.toFloat(); Serial.printf("Set BiasVolt to: %.2f mV\n", pImpedanceCfg->BiasVolt); - } else if (param == "rtia") { + } + else if (param == "rtia") + { // Set TIA (Transimpedance Amplifier) feedback resistor value - uint32_t rtia = DEFAULT_RTIA; // default value + uint32_t rtia = DEFAULT_RTIA; // default value int valueTest = value.toInt(); Serial.print("Value:"); Serial.println(value); // Map string values to corresponding constants - if (valueTest == 0) rtia = HSTIARTIA_200; - else if (valueTest == 1) rtia = HSTIARTIA_1K; - else if (valueTest == 2) rtia = HSTIARTIA_5K; - else if (valueTest == 3) rtia = HSTIARTIA_10K; - else if (valueTest == 4) rtia = HSTIARTIA_20K; - else if (valueTest == 5) rtia = HSTIARTIA_40K; - else if (valueTest == 6) rtia = HSTIARTIA_80K; - else if (valueTest == 7) rtia = HSTIARTIA_160K; - else Serial.print("no change to tia"); + if (valueTest == 0) + rtia = HSTIARTIA_200; + else if (valueTest == 1) + rtia = HSTIARTIA_1K; + else if (valueTest == 2) + rtia = HSTIARTIA_5K; + else if (valueTest == 3) + rtia = HSTIARTIA_10K; + else if (valueTest == 4) + rtia = HSTIARTIA_20K; + else if (valueTest == 5) + rtia = HSTIARTIA_40K; + else if (valueTest == 6) + rtia = HSTIARTIA_80K; + else if (valueTest == 7) + rtia = HSTIARTIA_160K; + else + Serial.print("no change to tia"); pImpedanceCfg->HstiaRtiaSel = rtia; Serial.printf("Set RTIA to: %s\n", value.c_str()); currentTIA = rtia; Serial.print("currentTIA: "); Serial.println(currentTIA); - - } else if (param == "pga") { + } + else if (param == "pga") + { // Set ADC Programmable Gain Amplifier gain - uint32_t pga = ADCPGA_1; // default value (1x gain) + uint32_t pga = ADCPGA_1; // default value (1x gain) // Map string values to corresponding constants - if (value == "1") pga = ADCPGA_1; - else if (value == "1.5") pga = ADCPGA_1P5; - else if (value == "2") pga = ADCPGA_2; - else if (value == "4") pga = ADCPGA_4; - else if (value == "9") pga = ADCPGA_9; + if (value == "1") + pga = ADCPGA_1; + else if (value == "1.5") + pga = ADCPGA_1P5; + else if (value == "2") + pga = ADCPGA_2; + else if (value == "4") + pga = ADCPGA_4; + else if (value == "9") + pga = ADCPGA_9; pImpedanceCfg->AdcPgaGain = pga; Serial.printf("Set PGA gain to: %s\n", value.c_str()); - } else if (param == "dftnum") { + } + else if (param == "dftnum") + { // Set DFT (Discrete Fourier Transform) number of points - uint32_t dft = DFTNUM_4096; // default value (4096 points) + uint32_t dft = DFTNUM_4096; // default value (4096 points) // Map string values to corresponding constants - if (value == "4096") dft = DFTNUM_4096; - else if (value == "2048") dft = DFTNUM_2048; - else if (value == "1024") dft = DFTNUM_1024; - else if (value == "512") dft = DFTNUM_512; - else if (value == "256") dft = DFTNUM_256; + if (value == "4096") + dft = DFTNUM_4096; + else if (value == "2048") + dft = DFTNUM_2048; + else if (value == "1024") + dft = DFTNUM_1024; + else if (value == "512") + dft = DFTNUM_512; + else if (value == "256") + dft = DFTNUM_256; pImpedanceCfg->DftNum = dft; Serial.printf("Set DFT number to: %s\n", value.c_str()); - } else if (param == "avgnum") { + } + else if (param == "avgnum") + { // Set ADC averaging number (how many samples to average) - uint32_t avg = ADCAVGNUM_16; // default value (16 samples) + uint32_t avg = ADCAVGNUM_16; // default value (16 samples) // Map string values to corresponding constants - if (value == "2") avg = ADCAVGNUM_2; - else if (value == "4") avg = ADCAVGNUM_4; - else if (value == "8") avg = ADCAVGNUM_8; - else if (value == "16") avg = ADCAVGNUM_16; + if (value == "2") + avg = ADCAVGNUM_2; + else if (value == "4") + avg = ADCAVGNUM_4; + else if (value == "8") + avg = ADCAVGNUM_8; + else if (value == "16") + avg = ADCAVGNUM_16; pImpedanceCfg->ADCAvgNum = avg; Serial.printf("Set ADC average number to: %s\n", value.c_str()); - } else if (param == "sweep") { + } + else if (param == "sweep") + { // Enable/disable frequency sweep mode bool enableSweep = (value == "1" || value.equalsIgnoreCase("true")); pImpedanceCfg->SweepCfg.SweepEn = enableSweep ? bTRUE : bFALSE; Serial.printf("Set sweep enable to: %d\n", pImpedanceCfg->SweepCfg.SweepEn); - } else if (param == "sweepstart") { + } + else if (param == "sweepstart") + { // Set sweep start frequency (Hz) pImpedanceCfg->SweepCfg.SweepStart = value.toFloat(); Serial.printf("Set sweep start frequency to: %.2f Hz\n", pImpedanceCfg->SweepCfg.SweepStart); - } else if (param == "sweepstop") { + } + else if (param == "sweepstop") + { // Set sweep stop frequency (Hz) pImpedanceCfg->SweepCfg.SweepStop = value.toFloat(); Serial.printf("Set sweep stop frequency to: %.2f Hz\n", pImpedanceCfg->SweepCfg.SweepStop); - } else if (param == "sweeppoints") { + } + else if (param == "sweeppoints") + { // Set number of frequency points in the sweep pImpedanceCfg->SweepCfg.SweepPoints = value.toInt(); Serial.printf("Set sweep points to: %d\n", pImpedanceCfg->SweepCfg.SweepPoints); - } else if (param == "sweeplog") { + } + else if (param == "sweeplog") + { // Set logarithmic (true) or linear (false) frequency spacing bool logSpacing = (value == "1" || value.equalsIgnoreCase("true")); pImpedanceCfg->SweepCfg.SweepLog = logSpacing ? bTRUE : bFALSE; Serial.printf("Set sweep log mode to: %d\n", pImpedanceCfg->SweepCfg.SweepLog); - } else { + } + else + { // Handle unrecognized parameter Serial.println("Malformed command: unrecognized parameter"); return; @@ -715,19 +842,19 @@ saveCurrentConfig(); } - /** * @brief Displays impedance measurement results - * + * * This function formats and outputs impedance measurement data to the serial port. * It retrieves the current measurement frequency and prints the magnitude and phase * of each impedance measurement in the data array. - * changed to enable just outputting every Nth result for debugging - * + * changed to enable just outputting every Nth result for debugging + * * @param pData Pointer to array of impedance data (as fImpPol_Type structures) * @param DataCount Number of impedance measurements in the array */ -void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount, int readingCount) { +void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount, int readingCount) +{ // Variable to store measurement frequency float freq; // Cast raw data pointer to impedance polar format type @@ -737,10 +864,10 @@ AppIMPCtrl(IMPCTRL_GETFREQ, &freq); // Print measurement frequency - //Serial.printf("Freq: %.2f Hz ", freq); + // Serial.printf("Freq: %.2f Hz ", freq); // Print the number of data points - //Serial.printf("DataPoints: %lu ", DataCount); + // Serial.printf("DataPoints: %lu ", DataCount); // // Process and print each impedance measurement // for (uint32_t i = 0; i < DataCount; i++) { @@ -759,19 +886,19 @@ magnitudeArray[readingCount] = pImp[0].Magnitude; phaseArray[readingCount] = pImp[0].Phase * 180 / MATH_PI; // Add newline for better readability in serial monitor - //Serial.println(); + // Serial.println(); } - /** * @brief Initializes the AD5940 impedance measurement configuration structure - * + * * This function sets default values for the impedance measurement configuration * structure including sequence settings, frequency, switch matrix connections, * gain settings, filtering, and DFT parameters. It configures the AD5940 for * basic impedance measurements with appropriate default settings. */ -void AD5940ImpedanceStructInit(void) { +void AD5940ImpedanceStructInit(void) +{ // Pointer to the impedance configuration structure AppIMPCfg_Type *pImpedanceCfg; @@ -783,41 +910,41 @@ //---------- Sequence Configuration ---------- // Set sequence memory allocation - pImpedanceCfg->SeqStartAddr = 0; // Start address in SRAM - pImpedanceCfg->MaxSeqLen = 512; // Maximum sequence length + pImpedanceCfg->SeqStartAddr = 0; // Start address in SRAM + pImpedanceCfg->MaxSeqLen = 512; // Maximum sequence length //---------- Measurement Parameters ---------- - pImpedanceCfg->RcalVal = REF_RESISTOR_VALUE; // calibration resistor - pImpedanceCfg->SinFreq = DEFAULT_FREQ; // 10kHz sine wave frequency - pImpedanceCfg->FifoThresh = DEFAULT_FIFO_THRESH; // FIFO threshold for interrupt + pImpedanceCfg->RcalVal = REF_RESISTOR_VALUE; // calibration resistor + pImpedanceCfg->SinFreq = DEFAULT_FREQ; // 10kHz sine wave frequency + pImpedanceCfg->FifoThresh = DEFAULT_FIFO_THRESH; // FIFO threshold for interrupt //---------- Switch Matrix Configuration ---------- // Configure switch matrix connections for the measurement path // Connect AIN2 and AIN3 pins to the HSTIA (High-Speed TIA) inputs - pImpedanceCfg->DswitchSel = DEFAULT_DSWITCH_CON; // D switch to AIN2 - pImpedanceCfg->PswitchSel = DEFAULT_PSWITCH_CON; // P switch to AIN2 - pImpedanceCfg->NswitchSel = DEFAULT_NSWITCH_CON; // N switch to AIN3 - pImpedanceCfg->TswitchSel = DEFAULT_TSWITCH_CON; // T switch to AIN3 and RTIA + pImpedanceCfg->DswitchSel = DEFAULT_DSWITCH_CON; // D switch to AIN2 + pImpedanceCfg->PswitchSel = DEFAULT_PSWITCH_CON; // P switch to AIN2 + pImpedanceCfg->NswitchSel = DEFAULT_NSWITCH_CON; // N switch to AIN3 + pImpedanceCfg->TswitchSel = DEFAULT_TSWITCH_CON; // T switch to AIN3 and RTIA //---------- Gain and Filtering Configuration ---------- // Set gain and filtering parameters for accurate measurements // Note: Using 200Ω RTIA to handle low impedance sensors without saturation - pImpedanceCfg->HstiaRtiaSel = currentTIA; // 200Ω TIA feedback resistor - pImpedanceCfg->ADCAvgNum = ADCAVGNUM_16; // Average 16 ADC samples + pImpedanceCfg->HstiaRtiaSel = currentTIA; // 200Ω TIA feedback resistor + pImpedanceCfg->ADCAvgNum = ADCAVGNUM_16; // Average 16 ADC samples //---------- Sweep Configuration ---------- // Disable frequency sweep by default (single frequency measurement) pImpedanceCfg->SweepCfg.SweepEn = bFALSE; //---------- Power Mode Configuration ---------- // Set high-power mode for optimal performance at frequencies > 80kHz - pImpedanceCfg->PwrMod = AFEPWR_HP; // High power mode + pImpedanceCfg->PwrMod = AFEPWR_HP; // High power mode //---------- Signal Processing Configuration ---------- // Configure decimation filters and DFT parameters - pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_2; // Sinc3 filter decimation ratio: 2 - // (ADC sampling rate: 800kSPS/2 = 400kSPS) - pImpedanceCfg->DftNum = DEFAULT_DFTNUM; // 4096-point DFT - pImpedanceCfg->DftSrc = DFTSRC_SINC3; // DFT input from Sinc3 filter + pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_2; // Sinc3 filter decimation ratio: 2 + // (ADC sampling rate: 800kSPS/2 = 400kSPS) + pImpedanceCfg->DftNum = DEFAULT_DFTNUM; // 4096-point DFT + pImpedanceCfg->DftSrc = DFTSRC_SINC3; // DFT input from Sinc3 filter // Mark configuration as changed so it will be applied pImpedanceCfg->bParaChanged = bTRUE; @@ -828,18 +955,19 @@ } /** * @brief Configures the AD5940 platform settings including clock, FIFO, interrupts, and GPIO - * + * * This function performs the low-level platform configuration of the AD5940 AFE. * It sets up the system clocks, FIFO buffer, interrupt controller, and GPIO pins * required for proper operation of the measurement system. - * + * * @return int32_t Returns 0 on success */ -static int32_t AD5940PlatformCfg(void) { +static int32_t AD5940PlatformCfg(void) +{ // Configuration structures - CLKCfg_Type clk_cfg; // Clock configuration - FIFOCfg_Type fifo_cfg; // FIFO configuration - AGPIOCfg_Type gpio_cfg; // GPIO configuration + CLKCfg_Type clk_cfg; // Clock configuration + FIFOCfg_Type fifo_cfg; // FIFO configuration + AGPIOCfg_Type gpio_cfg; // GPIO configuration // Hardware reset is commented out as it's handled elsewhere // AD5940_HWReset(); @@ -849,42 +977,43 @@ // Initialize SPI communication with AD5940 SPI.begin(); - //Serial.println("chipRead:"); + // Serial.println("chipRead:"); uint32_t testResult = AD5940_ReadReg(REG_AFECON_CHIPID); - //Serial.println(testResult); + // Serial.println(testResult); - if (testResult == 0 || testResult == 65535) { - Serial.println("System Cannot detect AD5940/1"); - Serial.println(testResult); + if (testResult == 0 || testResult == 65535) + { + // Serial.println("System Cannot detect AD5940/1"); + // Serial.println(testResult); SPI.end(); - return 1; //chip not present + return 1; // chip not present } - //Serial.println(AD5940_ReadReg(REG_AFECON_CHIPID)); - //Serial.println("ENDchipRead"); - // Initialize AD5940 core functions + // Serial.println(AD5940_ReadReg(REG_AFECON_CHIPID)); + // Serial.println("ENDchipRead"); + // Initialize AD5940 core functions AD5940_Initialize(); //---------- Clock Configuration ---------- // Set up system and ADC clocks - clk_cfg.ADCClkDiv = ADCCLKDIV_1; // No division for ADC clock - clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC; // ADC clock from high-frequency oscillator - clk_cfg.SysClkDiv = SYSCLKDIV_1; // No division for system clock - clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC; // System clock from high-frequency oscillator - clk_cfg.HfOSC32MHzMode = bFALSE; // Use 16MHz mode for HFOSC - clk_cfg.HFOSCEn = bTRUE; // Enable high-frequency oscillator - clk_cfg.HFXTALEn = bFALSE; // Disable high-frequency crystal - clk_cfg.LFOSCEn = bTRUE; // Enable low-frequency oscillator + clk_cfg.ADCClkDiv = ADCCLKDIV_1; // No division for ADC clock + clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC; // ADC clock from high-frequency oscillator + clk_cfg.SysClkDiv = SYSCLKDIV_1; // No division for system clock + clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC; // System clock from high-frequency oscillator + clk_cfg.HfOSC32MHzMode = bFALSE; // Use 16MHz mode for HFOSC + clk_cfg.HFOSCEn = bTRUE; // Enable high-frequency oscillator + clk_cfg.HFXTALEn = bFALSE; // Disable high-frequency crystal + clk_cfg.LFOSCEn = bTRUE; // Enable low-frequency oscillator // Apply clock configuration AD5940_CLKCfg(&clk_cfg); //---------- FIFO Configuration ---------- // Configure FIFO buffer for measurement data - fifo_cfg.FIFOEn = bFALSE; // Disable FIFO during configuration - fifo_cfg.FIFOMode = FIFOMODE_FIFO; // Use standard FIFO mode (not stream) - fifo_cfg.FIFOSize = FIFOSIZE_4KB; // Allocate 4KB for FIFO (2KB for sequencer) - fifo_cfg.FIFOSrc = FIFOSRC_DFT; // FIFO source is DFT results - fifo_cfg.FIFOThresh = 4; // Threshold for FIFO interrupt + fifo_cfg.FIFOEn = bFALSE; // Disable FIFO during configuration + fifo_cfg.FIFOMode = FIFOMODE_FIFO; // Use standard FIFO mode (not stream) + fifo_cfg.FIFOSize = FIFOSIZE_4KB; // Allocate 4KB for FIFO (2KB for sequencer) + fifo_cfg.FIFOSrc = FIFOSRC_DFT; // FIFO source is DFT results + fifo_cfg.FIFOThresh = 4; // Threshold for FIFO interrupt // Apply FIFO configuration AD5940_FIFOCfg(&fifo_cfg); @@ -897,7 +1026,7 @@ // Configure interrupt controller for data acquisition // Enable all interrupts in INTC1 for debugging/status checking - AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ADCMAXERR, bTRUE); // change to only adc max testing on intc1 + AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ADCMAXERR, bTRUE); // change to only adc max testing on intc1 AD5940_INTCClrFlag(AFEINTSRC_ALLINT); // Configure INTC0 for FIFO threshold interrupt only @@ -906,37 +1035,38 @@ //---------- GPIO Configuration ---------- // Configure AD5940 GPIO pins for specific functions - gpio_cfg.FuncSet = GP0_INT | GP1_SLEEP | GP2_SYNC; // GP0: Interrupt, GP1: Sleep, GP2: Sync - gpio_cfg.InputEnSet = 0; // No GPIO as inputs - gpio_cfg.OutputEnSet = AGPIO_Pin0 | AGPIO_Pin1 | AGPIO_Pin2; // Enable output on GP0-GP2 - gpio_cfg.OutVal = 0; // Initial output values low - gpio_cfg.PullEnSet = 0; // No pull-up/down resistors + gpio_cfg.FuncSet = GP0_INT | GP1_SLEEP | GP2_SYNC; // GP0: Interrupt, GP1: Sleep, GP2: Sync + gpio_cfg.InputEnSet = 0; // No GPIO as inputs + gpio_cfg.OutputEnSet = AGPIO_Pin0 | AGPIO_Pin1 | AGPIO_Pin2; // Enable output on GP0-GP2 + gpio_cfg.OutVal = 0; // Initial output values low + gpio_cfg.PullEnSet = 0; // No pull-up/down resistors // Apply GPIO configuration AD5940_AGPIOCfg(&gpio_cfg); // Unlock sleep mode to allow power management AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); - return 0; // Return success + return 0; // Return success } /** * @brief Initializes the hardware and software components of the measurement system - * + * * This function performs the complete startup sequence required to initialize - * the AD5940 AFE and the measurement system. It configures the GPIO pins, + * the AD5940 AFE and the measurement system. It configures the GPIO pins, * resets the AD5940, sets up the interrupt handling, and initializes the * impedance measurement application. */ -bool startupAD5941() { +bool startupAD5941() +{ // Initial delay to ensure power stabilization delay(100); //---------- GPIO Configuration ---------- // Configure SPI chip select pin pinMode(TEENSY_SPI_CS_PIN, OUTPUT); - digitalWrite(TEENSY_SPI_CS_PIN, HIGH); // Deselect AD5940 initially + digitalWrite(TEENSY_SPI_CS_PIN, HIGH); // Deselect AD5940 initially //---------- AD5940 Reset Sequence ---------- // Configure reset pin @@ -945,15 +1075,15 @@ // Hardware reset sequence for AD5940 digitalWriteFast(TEENSY_RESET_PIN, HIGH); - delay(1); // Wait for reset line to stabilize + delay(1); // Wait for reset line to stabilize digitalWriteFast(TEENSY_RESET_PIN, LOW); - delay(1); // Hold reset active + delay(1); // Hold reset active digitalWriteFast(TEENSY_RESET_PIN, HIGH); - delay(1); // Allow time for AD5940 to initialize + delay(1); // Allow time for AD5940 to initialize // Ensure SPI chip select is properly configured pinMode(TEENSY_SPI_CS_PIN, OUTPUT); - digitalWrite(TEENSY_SPI_CS_PIN, HIGH); // Deselect AD5940 + digitalWrite(TEENSY_SPI_CS_PIN, HIGH); // Deselect AD5940 //---------- Interrupt Configuration ---------- // Configure AD5940 interrupt pin with internal pull-up @@ -963,14 +1093,15 @@ // Attach interrupt handler to respond to AD5940 interrupts attachInterrupt(digitalPinToInterrupt(AD5940_INT_PIN), AD5940_InterruptHandler, - FALLING); // Trigger on falling edge + FALLING); // Trigger on falling edge //---------- AD5940 Initialization ---------- // Initialize AD5940 platform-specific configurations // (SPI communication, timing, etc.) - if (AD5940PlatformCfg()) { - return true; // startupfailed + if (AD5940PlatformCfg()) + { + return true; // startupfailed } // Initialize impedance measurement configuration structure @@ -982,30 +1113,31 @@ // Ensure measurement system is in a known stopped state AppIMPCtrl(IMPCTRL_STOPSYNC, 0); - resetSwitchMatrix(); // Disconnect all switches + resetSwitchMatrix(); // Disconnect all switches // Save the initial configuration saveCurrentConfig(); return false; // Confirm initialization complete and display user information - //Serial.println("Startup Complete"); - //printHelp(); // Show available commands - //printCurrentConfigShort(); // Display current measurement settings + // Serial.println("Startup Complete"); + // printHelp(); // Show available commands + // printCurrentConfigShort(); // Display current measurement settings // System is now ready for measurements // Serial.println("System ready; enter b to perform reading"); } /** * @brief Performs a single impedance measurement and displays the result - * + * * This function conducts a complete impedance measurement cycle using the AD5940. * It initializes the measurement buffer, starts the measurement, waits for * completion, processes the data, and displays the results. */ -bool AppIMPMeasure(int readingCount) { - uint32_t bufferSize; // Variable to track valid data in buffer - int trigCount = 0; // Counter for completed measurements - unsigned long startTime; // For timeout tracking - const unsigned long TIMEOUT_MS = 5000; // 5 second timeout (adjust as needed) +bool AppIMPMeasure(int readingCount) +{ + uint32_t bufferSize; // Variable to track valid data in buffer + int trigCount = 0; // Counter for completed measurements + unsigned long startTime; // For timeout tracking + const unsigned long TIMEOUT_MS = 5000; // 5 second timeout (adjust as needed) bool timeoutOccurred = false; resetSwitchMatrix(); AppIMPInit(AppBuff, APPBUFF_SIZE); @@ -1018,7 +1150,7 @@ // // Stop any ongoing measurement before starting a new one // AppIMPCtrl(IMPCTRL_STOPSYNC, 0); - //AD5940_Delay10us(5000); // 50ms settling + // AD5940_Delay10us(5000); // 50ms settling // Start the impedance measurement AppIMPCtrl(IMPCTRL_START, 0); @@ -1027,22 +1159,25 @@ startTime = millis(); // Wait for one measurement cycle to complete or timeout trigCount = 0; - while (trigCount < 1) { + while (trigCount < 1) + { // Check for timeout - if (millis() - startTime > TIMEOUT_MS) { + if (millis() - startTime > TIMEOUT_MS) + { timeoutOccurred = true; break; } // Check if interrupt flag is set (measurement data ready) - if (AD5940_GetMCUIntFlag() == 1) { + if (AD5940_GetMCUIntFlag() == 1) + { // Clear the interrupt flag AD5940_ClrMCUIntFlag(); // Set up buffer for data collection and process the measurement bufferSize = APPBUFF_SIZE; AppIMPISR(AppBuff, &bufferSize); - //Serial.println("tset"); + // Serial.println("tset"); // Display the impedance measurement results // bufferSize now contains the actual number of valid data points @@ -1057,21 +1192,23 @@ AppIMPCtrl(IMPCTRL_STOPSYNC, 0); // Handle timeout situation - if (timeoutOccurred) { - Serial.println("ERROR: Impedance measurement timeout occurred!"); + if (timeoutOccurred) + { + // Serial.println("ERROR: Impedance measurement timeout occurred!"); } return timeoutOccurred; } /** * @brief Displays the current conductivity measurement configuration settings - * + * * This function retrieves the current configuration settings from the * impedance measurement module and prints a summary of the key parameters * to the serial monitor. It shows the essential parameters related to * excitation signal and bias voltage. */ -void printCurrentConfigShort() { +void printCurrentConfigShort() +{ // Pointer to store configuration structure AppIMPCfg_Type *cfg; @@ -1083,29 +1220,30 @@ Serial.println("Current Conductivity Settings"); // Print essential measurement parameters with units - Serial.printf("SinFreq: %.2f Hz\n", cfg->SinFreq); // Sine wave frequency - Serial.printf("DacVoltPP: %.2f mV\n", cfg->DacVoltPP); // DAC peak-to-peak voltage - Serial.printf("BiasVolt: %.2f mV\n", cfg->BiasVolt); // DC bias voltage + Serial.printf("SinFreq: %.2f Hz\n", cfg->SinFreq); // Sine wave frequency + Serial.printf("DacVoltPP: %.2f mV\n", cfg->DacVoltPP); // DAC peak-to-peak voltage + Serial.printf("BiasVolt: %.2f mV\n", cfg->BiasVolt); // DC bias voltage // Additional parameters (commented out for simplified display) // Uncomment these lines to display more detailed configuration - Serial.printf("RTIA: %lu\n", cfg->HstiaRtiaSel); // Transimpedance amplifier setting - Serial.printf("PGA Gain: %lu\n", cfg->AdcPgaGain); // Programmable gain amplifier setting - Serial.printf("DFT Points: %lu\n", cfg->DftNum); // Number of DFT points - Serial.printf("ADC Avg: %u\n", cfg->ADCAvgNum); // ADC averaging setting + Serial.printf("RTIA: %lu\n", cfg->HstiaRtiaSel); // Transimpedance amplifier setting + Serial.printf("PGA Gain: %lu\n", cfg->AdcPgaGain); // Programmable gain amplifier setting + Serial.printf("DFT Points: %lu\n", cfg->DftNum); // Number of DFT points + Serial.printf("ADC Avg: %u\n", cfg->ADCAvgNum); // ADC averaging setting // Print footer with separator Serial.println("------------------------"); } /** * @brief Displays available commands and parameters to the serial monitor - * + * * This function prints a formatted help menu with all available commands, * their descriptions, and configuration parameters to guide users on how * to control the measurement system via the serial interface. */ -void printHelp() { +void printHelp() +{ Serial.println("------------------"); Serial.println("Version:"); @@ -1148,18 +1286,20 @@ /** * @brief Reads and processes commands from the serial interface - * + * * This function reads incoming commands from the serial port and processes them based * on the first character or prefix. It handles configuration commands, measurement * requests, help display, and system control functions. */ -void readCommand() { +void readCommand() +{ // Read a line from the serial port until newline character String command = Serial.readStringUntil('\n'); // Handle configuration commands (format: cfg,parameter,value) - if (command.startsWith("cfg,")) { + if (command.startsWith("cfg,")) + { handleConfigCommand(command); Serial.println(command); Serial.print("*"); @@ -1169,310 +1309,395 @@ } // Handle save command for doubles (format: save,value1,value2,value3,value4) - if (command.startsWith("save,")) { + if (command.startsWith("save,")) + { handleSaveCommand(command); return; } // Handle load command for doubles (format: load) - if (command.startsWith("load")) { + if (command.startsWith("load")) + { handleLoadCommand(); return; } // Process commands based on the first character - switch (command.charAt(0)) { - case '?': // Help command - display available commands + switch (command.charAt(0)) + { + case '?': // Help command - display available commands + { + printHelp(); + } + break; + case 'a': + { + isInitialized = false; + while(!initializeSelectedUnits()); + if ( true == isInitialized) + { + getInitStatus(); + } + selectFirstWorkingUnit(); + // Serial.print("*"); + } + break; + case '-': + { + // if (AD5940_IsAdcMaxSaturatedAndClear()) { + // Serial.println("SaturationFlag"); + // } else { + // Serial.println("noSat"); + // } + } + break; + case '+': + { + // AD5940_EnableAdcMaxSaturationIRQ(/*max_code=*/0xFFFF, /*hysteresis=*/0x0080); + Serial.println("rearm"); + } + break; + case 'b': + { + measureAllSuccessfulUnits(); + Serial.print("*"); + } + break; + case 'c': + { + // test case only + // AppRTDMeasure(rtdVoltageValue); + // AppIMPMeasure(0); + } + break; + case 'd': // Perform impedance measurement followed by RTD measurement + { + + // quick patch for fixing spontaneous measurements + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= jitterTimeout) + { + + // Run multiple settling measurements in a loop + for (int settling = 0; settling < numSettlingMeasurements; settling++) { - printHelp(); + if (settling == 0) + { + Serial.println("Performing settling measurements..."); + } + if (verboseMode) + { + Serial.printf(" Settling Measurement#: %u ", settling); + Serial.println(" "); + } + // Run one complete measurement cycle but discard results + AppIMPInit(AppBuff, APPBUFF_SIZE); + AppIMPCtrl(IMPCTRL_START, 0); + + // Wait for completion (simplified polling) + int timeout_count = 0; + while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) + { + delay(1); + timeout_count++; + } + + if (AD5940_GetMCUIntFlag() == 1) + { + AD5940_ClrMCUIntFlag(); + uint32_t dummy_size = APPBUFF_SIZE; + AppIMPISR(AppBuff, &dummy_size); // Discard this data + } + + AppIMPCtrl(IMPCTRL_STOPSYNC, 0); + + // Small delay between settling measurements + if (settling < numSettlingMeasurements - 1) + { + AD5940_Delay10us(1000); // 10ms between measurements + } } - break; - case 'a': + if (verboseMode) { - initializeSelectedUnits(); - selectFirstWorkingUnit(); - Serial.print("*"); + Serial.println(" End Settling Measurements"); } - break; - case '-': + } + + if (verboseMode) + { + Serial.println("Conductivity"); + Serial.print(" Conductivity TIA Resistor: "); + switch (DEFAULT_RTIA) { - // if (AD5940_IsAdcMaxSaturatedAndClear()) { - // Serial.println("SaturationFlag"); - // } else { - // Serial.println("noSat"); - // } + case HSTIARTIA_200: + Serial.println("200 Ohm"); + break; + case HSTIARTIA_1K: + Serial.println("1K Ohm"); + break; + case HSTIARTIA_5K: + Serial.println("5K Ohm"); + break; + case HSTIARTIA_10K: + Serial.println("10K Ohm"); + break; + case HSTIARTIA_20K: + Serial.println("20K Ohm"); + break; + case HSTIARTIA_40K: + Serial.println("40K Ohm"); + break; + case HSTIARTIA_80K: + Serial.println("80K Ohm"); + break; + case HSTIARTIA_160K: + Serial.println("160K Ohm"); + break; + default: + Serial.println("Unknown"); + break; } - break; - case '+': + } + // Perform impedance measurement + for (int i = 0; i < repeatNumber; i++) + { + AppIMPMeasure(i); + } + if (verboseMode) + { + if (AD5940_IsAdcMaxSaturatedAndClear()) { - // AD5940_EnableAdcMaxSaturationIRQ(/*max_code=*/0xFFFF, /*hysteresis=*/0x0080); - Serial.println("rearm"); + Serial.println("SaturationFlag tripped!"); } - break; - case 'b': + else { - measureAllSuccessfulUnits(); - Serial.print("*"); + Serial.println("SaturationFlag NOT tripped!"); } - break; - case 'c': - { - // test case only - // AppRTDMeasure(rtdVoltageValue); - // AppIMPMeasure(0); - } - break; - case 'd': // Perform impedance measurement followed by RTD measurement - { + } + float medianMag; + float medianPhase; + // take median and output results + if (repeatNumber == 1) + { + medianMag = magnitudeArray[0]; + medianPhase = phaseArray[0]; + } + else + { + medianMag = calculateMedian(magnitudeArray, repeatNumber); + medianPhase = calculateMedian(phaseArray, repeatNumber); + } - // quick patch for fixing spontaneous measurements - unsigned long currentMillis = millis(); - if (currentMillis - previousMillis >= jitterTimeout) { + Serial.printf("Freq: %.2f Hz ", storedFrequency); - // Run multiple settling measurements in a loop - for (int settling = 0; settling < numSettlingMeasurements; settling++) { - if (settling == 0) { - Serial.println("Performing settling measurements..."); - } - if (verboseMode) { - Serial.printf(" Settling Measurement#: %u ", settling); - Serial.println(" "); - } - // Run one complete measurement cycle but discard results - AppIMPInit(AppBuff, APPBUFF_SIZE); - AppIMPCtrl(IMPCTRL_START, 0); + // Print the number of data points + Serial.printf("DataPoints: %lu ", 1); - // Wait for completion (simplified polling) - int timeout_count = 0; - while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) { - delay(1); - timeout_count++; - } + // Print magnitude in ohms and phase in degrees + // Note: Converting phase from radians to degrees (phase * 180 / π) + Serial.printf("RzMag: %.2f Ohm, RzPhase: %.2f deg", + medianMag, + medianPhase); - if (AD5940_GetMCUIntFlag() == 1) { - AD5940_ClrMCUIntFlag(); - uint32_t dummy_size = APPBUFF_SIZE; - AppIMPISR(AppBuff, &dummy_size); // Discard this data - } + // Add separator and perform RTD (temperature) measurement + Serial.print(";"); + AppRTDMeasure(rtdVoltageValue); + Serial.print("*"); + previousMillis = millis(); + } + break; + case 'f': + { + // Anything after the initial 't' (e.g. ",123.4" or " -12.3")? + if (command.length() > 1) + { + // Strip the leading 't' plus optional comma or space + String arg = command.substring(1); + arg.trim(); // remove whitespace + if (arg.startsWith(",")) + arg.remove(0, 1); + arg.trim(); - AppIMPCtrl(IMPCTRL_STOPSYNC, 0); + // Convert to float; String::toFloat() returns 0.0 if conversion fails + float parsed = arg.toFloat(); + if (arg.length() && !(isnan(parsed) || isinf(parsed))) + { + rtdVoltageValue = parsed; // valid number received + } + else + { + Serial.println("bad value, not changing rtdVoltageValue"); + } + } + Serial.println("rtdVoltageValue"); + Serial.println(rtdVoltageValue); + } + break; - // Small delay between settling measurements - if (settling < numSettlingMeasurements - 1) { - AD5940_Delay10us(1000); // 10ms between measurements - } - } - if (verboseMode) { - Serial.println(" End Settling Measurements"); - } - } + case 't': // Read temperature at a user-defined RTD drive level (mV) + { - if (verboseMode) { - Serial.println("Conductivity"); - Serial.print(" Conductivity TIA Resistor: "); - switch (DEFAULT_RTIA) { - case HSTIARTIA_200: Serial.println("200 Ohm"); break; - case HSTIARTIA_1K: Serial.println("1K Ohm"); break; - case HSTIARTIA_5K: Serial.println("5K Ohm"); break; - case HSTIARTIA_10K: Serial.println("10K Ohm"); break; - case HSTIARTIA_20K: Serial.println("20K Ohm"); break; - case HSTIARTIA_40K: Serial.println("40K Ohm"); break; - case HSTIARTIA_80K: Serial.println("80K Ohm"); break; - case HSTIARTIA_160K: Serial.println("160K Ohm"); break; - default: Serial.println("Unknown"); break; - } - } - // Perform impedance measurement - for (int i = 0; i < repeatNumber; i++) { - AppIMPMeasure(i); - } - if (verboseMode) { - if (AD5940_IsAdcMaxSaturatedAndClear()) { - Serial.println("SaturationFlag tripped!"); - } else { - Serial.println("SaturationFlag NOT tripped!"); - } - } - float medianMag; - float medianPhase; - // take median and output results - if (repeatNumber == 1) { - medianMag = magnitudeArray[0]; - medianPhase = phaseArray[0]; - } else { - medianMag = calculateMedian(magnitudeArray, repeatNumber); - medianPhase = calculateMedian(phaseArray, repeatNumber); - } + AppRTDMeasure(rtdVoltageValue); + Serial.print("*"); // optional “command complete” asterisk + } + case 's': // Perform system startup and configuration + { + // Run startup initialization + startupAD5941(); - Serial.printf("Freq: %.2f Hz ", storedFrequency); + // Save the configuration and stop any ongoing measurement + saveCurrentConfig(); + AppIMPCtrl(IMPCTRL_STOPSYNC, 0); + Serial.println("Startup Complete"); + } + break; + case 'p': + { + // propagate settings change through all units + Serial.println("function removed"); + Serial.println("*"); + } + case 'j': // Perform system startup and configuration + { + selectUnit(command.substring(2).toInt()); + } + break; - // Print the number of data points - Serial.printf("DataPoints: %lu ", 1); + case 'r': // Restart the microcontroller + { + _reboot_Teensyduino_(); + } + break; - // Print magnitude in ohms and phase in degrees - // Note: Converting phase from radians to degrees (phase * 180 / π) - Serial.printf("RzMag: %.2f Ohm, RzPhase: %.2f deg", - medianMag, - medianPhase); + case 'x': // Echo test command + { + Serial.println("z"); + Serial.println("*"); + } + break; + case 'v': // Toggle verbose mode on/off + { + verboseMode = !verboseMode; + Serial.print("Verbose mode "); + Serial.println(verboseMode ? "ON" : "OFF"); + } + break; + case 'y': // Print current configuration in short format + { + printCurrentConfigShort(); + } + break; + case 'z': // Print current configuration in short format + { + Serial.print("AlyIDNumber: "); + // Read the 4 words (32-bits each) that make up the 128-bit serial number + uint32_t serNum[4]; + // OCOTP serial number registers + serNum[0] = HW_OCOTP_CFG0; // First 32 bits + serNum[1] = HW_OCOTP_CFG1; // Second 32 bits + serNum[2] = HW_OCOTP_CFG2; // Third 32 bits + serNum[3] = HW_OCOTP_CFG3; // Fourth 32 bits - // Add separator and perform RTD (temperature) measurement - Serial.print(";"); - AppRTDMeasure(rtdVoltageValue); - Serial.print("*"); - previousMillis = millis(); - } - break; - case 'e': - { - Serial.println("function removed"); - Serial.print("*"); - } - break; - case 'f': - { - // Anything after the initial 't' (e.g. ",123.4" or " -12.3")? - if (command.length() > 1) { - // Strip the leading 't' plus optional comma or space - String arg = command.substring(1); - arg.trim(); // remove whitespace - if (arg.startsWith(",")) arg.remove(0, 1); - arg.trim(); + // Print the serial number in hexadecimal format + for (int i = 0; i < 4; i++) + { + // Print each 32-bit word with leading zeros + char buf[9]; + snprintf(buf, sizeof(buf), "%08lX", serNum[i]); + Serial.print(buf); - // Convert to float; String::toFloat() returns 0.0 if conversion fails - float parsed = arg.toFloat(); - if (arg.length() && !(isnan(parsed) || isinf(parsed))) { - rtdVoltageValue = parsed; // valid number received - } else { - Serial.println("bad value, not changing rtdVoltageValue"); - } - } - Serial.println("rtdVoltageValue"); - Serial.println(rtdVoltageValue); - } - break; - - case 't': // Read temperature at a user-defined RTD drive level (mV) + // Add a separator between words (except after the last one) + if (i < 3) { - - AppRTDMeasure(rtdVoltageValue); - Serial.print("*"); // optional “command complete” asterisk + Serial.print("-"); } - case 's': // Perform system startup and configuration - { - // Run startup initialization - startupAD5941(); + } + Serial.println(); + } + break; - // Save the configuration and stop any ongoing measurement - saveCurrentConfig(); - AppIMPCtrl(IMPCTRL_STOPSYNC, 0); - Serial.println("Startup Complete"); - } - break; - case 'p': - { - //propagate settings change through all units - Serial.println("function removed"); - Serial.println("*"); - } - case 'j': // Perform system startup and configuration - { - selectUnit(command.substring(2).toInt()); - } - break; + case 'i': // blink LED on teensy to id unit + { + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); + SPI.begin(); // rerun SPI to initialize CLK pin (which is shared with LED_BUILTIN) + } + break; - case 'r': // Restart the microcontroller - { - _reboot_Teensyduino_(); - } - break; + case 'e': + { + // Transmit all the data stored in EEPROM + getEEPROMdata(); + } + break; - case 'x': // Echo test command - { - Serial.println("z"); - Serial.println("*"); - } - break; - case 'v': // Toggle verbose mode on/off - { - verboseMode = !verboseMode; - Serial.print("Verbose mode "); - Serial.println(verboseMode ? "ON" : "OFF"); - } - break; - case 'y': // Print current configuration in short format - { - printCurrentConfigShort(); - } - break; - case 'z': // Print current configuration in short format - { - Serial.print("AlyIDNumber: "); - // Read the 4 words (32-bits each) that make up the 128-bit serial number - uint32_t serNum[4]; + case 'g': + { + // Mesaure and transmit for all the initialized sensors + if(false == getAllMesaurements()) + { + Serial.write(0); + } + } + break; - // OCOTP serial number registers - serNum[0] = HW_OCOTP_CFG0; // First 32 bits - serNum[1] = HW_OCOTP_CFG1; // Second 32 bits - serNum[2] = HW_OCOTP_CFG2; // Third 32 bits - serNum[3] = HW_OCOTP_CFG3; // Fourth 32 bits + case 'h': + { + // Get single sensor data in binary format. + getSelectedSensorMeasurements(); + } + break; - // Print the serial number in hexadecimal format - for (int i = 0; i < 4; i++) { - // Print each 32-bit word with leading zeros - char buf[9]; - snprintf(buf, sizeof(buf), "%08lX", serNum[i]); - Serial.print(buf); + case 'k': + { + // Get conductivity mesaurement settings in binary format. + getMesaurementSettings(); + } + break; - // Add a separator between words (except after the last one) - if (i < 3) { - Serial.print("-"); - } - } - Serial.println(); - } - break; + case 'l': + { + // Get Initialization Status + getInitStatus(); + } + break; - case 'i': // blink LED on teensy to id unit - { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); - delay(500); - digitalWrite(LED_BUILTIN, LOW); - delay(500); - digitalWrite(LED_BUILTIN, HIGH); - delay(500); - digitalWrite(LED_BUILTIN, LOW); - delay(500); - SPI.begin(); // rerun SPI to initialize CLK pin (which is shared with LED_BUILTIN) - } - break; - case 'h': - { - Serial.println("function removed"); - Serial.println("*"); - } - break; - - case 'k': - { - Serial.println("function removed"); - Serial.println("*"); - } - break; - default: // Handle unknown commands - { - Serial.println("Unknown command. Type ? for help."); - } - break; + case 'm': + { + sendSensorData = true; + sendAllSensorData(); } + break; + case 'n': + { + sendSensorData = false; + sendAllSensorData(); + } + break; + + default: // Handle unknown commands + { + Serial.println("Unknown command. Type ? for help."); + } + break; + } } // Function to select a unit and update pins -void selectUnit(int unitIn) { +void selectUnit(int unitIn) +{ // Validate unit input (1-6) - if (unitIn < 1 || unitIn > 6) { - Serial.println("Error: Unit must be between 1 and 6"); + if (unitIn < 1 || unitIn > 6) + { + // Serial.println("Error: Unit must be between 1 and 6"); return; } @@ -1485,135 +1710,150 @@ pinMode(SELECT_C, OUTPUT); // Default state - disable all units - digitalWrite(EN_A, HIGH); // Disabled (active LOW) - digitalWrite(EN_B, HIGH); // Disabled - digitalWrite(EN_C, HIGH); // Disabled + digitalWrite(EN_A, HIGH); // Disabled (active LOW) + digitalWrite(EN_B, HIGH); // Disabled + digitalWrite(EN_C, HIGH); // Disabled digitalWrite(SELECT_A, LOW); digitalWrite(SELECT_B, LOW); digitalWrite(SELECT_C, LOW); // Update reset and interrupt pins based on selected unit - switch (unitIn) { - case 1: - TEENSY_RESET_PIN = 17; - AD5940_INT_PIN = 18; - digitalWrite(EN_A, LOW); // Enable unit 1 (A) - break; - case 2: - TEENSY_RESET_PIN = 22; - AD5940_INT_PIN = 21; - digitalWrite(EN_A, LOW); // Enable unit 2 (A) - digitalWrite(SELECT_A, HIGH); // Select unit 2 on mux A - break; - case 3: - TEENSY_RESET_PIN = 14; - AD5940_INT_PIN = 15; - digitalWrite(EN_B, LOW); // Enable unit 3 (B) - break; - case 4: - TEENSY_RESET_PIN = 3; - AD5940_INT_PIN = 4; - digitalWrite(EN_B, LOW); // Enable unit 4 (B) - digitalWrite(SELECT_B, HIGH); // Select unit 4 on mux B - break; - case 5: - TEENSY_RESET_PIN = 9; - AD5940_INT_PIN = 8; - digitalWrite(EN_C, LOW); // Enable unit 5 (C) - break; - case 6: - TEENSY_RESET_PIN = 0; - AD5940_INT_PIN = 1; - digitalWrite(EN_C, LOW); // Enable unit 6 (C) - digitalWrite(SELECT_C, HIGH); // Select unit 6 on mux C - break; + switch (unitIn) + { + case 1: + TEENSY_RESET_PIN = 17; + AD5940_INT_PIN = 18; + digitalWrite(EN_A, LOW); // Enable unit 1 (A) + break; + case 2: + TEENSY_RESET_PIN = 22; + AD5940_INT_PIN = 21; + digitalWrite(EN_A, LOW); // Enable unit 2 (A) + digitalWrite(SELECT_A, HIGH); // Select unit 2 on mux A + break; + case 3: + TEENSY_RESET_PIN = 14; + AD5940_INT_PIN = 15; + digitalWrite(EN_B, LOW); // Enable unit 3 (B) + break; + case 4: + TEENSY_RESET_PIN = 3; + AD5940_INT_PIN = 4; + digitalWrite(EN_B, LOW); // Enable unit 4 (B) + digitalWrite(SELECT_B, HIGH); // Select unit 4 on mux B + break; + case 5: + TEENSY_RESET_PIN = 9; + AD5940_INT_PIN = 8; + digitalWrite(EN_C, LOW); // Enable unit 5 (C) + break; + case 6: + TEENSY_RESET_PIN = 0; + AD5940_INT_PIN = 1; + digitalWrite(EN_C, LOW); // Enable unit 6 (C) + digitalWrite(SELECT_C, HIGH); // Select unit 6 on mux C + break; } // Configure the new reset and interrupt pins pinMode(TEENSY_RESET_PIN, OUTPUT); pinMode(AD5940_INT_PIN, INPUT); - Serial.print("Selected Unit: "); - Serial.print(unitIn); - Serial.print(" | Reset Pin: "); - Serial.print(TEENSY_RESET_PIN); - Serial.print(" | Interrupt Pin: "); - Serial.println(AD5940_INT_PIN); + + singleSensorPacket.sensorNum = unitIn; // Conductivity Sensor number i.e unit 1 to 6. + + // Serial.print("Selected Unit: "); + // Serial.print(unitIn); + // Serial.print(" | Reset Pin: "); + // Serial.print(TEENSY_RESET_PIN); + // Serial.print(" | Interrupt Pin: "); + // Serial.println(AD5940_INT_PIN); } -// Configuration: Number of double precision values (rest will be single precision) -#define DOUBLE_COUNT 8 // First 8 values as doubles, remaining as floats - // Write a mixed array (doubles first, then floats) to the EEPROM -void writeMixedValues(unsigned int baseAddress, double *doubleData, int doubleCount, float *floatData, int floatCount) { +void writeMixedValues(unsigned int baseAddress, double *doubleData, int doubleCount, float *floatData, int floatCount) +{ // Write doubles first - for (int i = 0; i < doubleCount; i++) { + for (int i = 0; i < doubleCount; i++) + { unsigned int address = baseAddress + (i * sizeof(double)); writeDouble(address, doubleData[i]); delay(10); } // Write floats after the doubles unsigned int floatBaseAddress = baseAddress + (doubleCount * sizeof(double)); - for (int i = 0; i < floatCount; i++) { + for (int i = 0; i < floatCount; i++) + { unsigned int address = floatBaseAddress + (i * sizeof(float)); writeFloat(address, floatData[i]); delay(10); } } // Read a mixed array (doubles first, then floats) from the EEPROM -void readMixedValues(unsigned int baseAddress, double *doubleData, int doubleCount, float *floatData, int floatCount) { +void readMixedValues(unsigned int baseAddress, double *doubleData, int doubleCount, float *floatData, int floatCount) +{ // Read doubles first - for (int i = 0; i < doubleCount; i++) { + for (int i = 0; i < doubleCount; i++) + { unsigned int address = baseAddress + (i * sizeof(double)); doubleData[i] = readDouble(address); } // Read floats after the doubles unsigned int floatBaseAddress = baseAddress + (doubleCount * sizeof(double)); - for (int i = 0; i < floatCount; i++) { + for (int i = 0; i < floatCount; i++) + { unsigned int address = floatBaseAddress + (i * sizeof(float)); floatData[i] = readFloat(address); } } // Write a single float to the EEPROM -void writeFloat(unsigned int address, float data) { +void writeFloat(unsigned int address, float data) +{ // Create a union to access the bytes of the float - union { + union + { float f; byte b[4]; } floatBytes; floatBytes.f = data; // Write each byte of the float - for (uint i = 0; i < sizeof(float); i++) { + for (uint i = 0; i < sizeof(float); i++) + { writeByte(address + i, floatBytes.b[i]); - delay(5); // Small delay between byte writes + delay(5); // Small delay between byte writes } } // Read a single float from the EEPROM -float readFloat(unsigned int address) { +float readFloat(unsigned int address) +{ // Create a union to build the float from bytes - union { + union + { float f; byte b[4]; } floatBytes; // Read each byte of the float - for (uint i = 0; i < sizeof(float); i++) { + for (uint i = 0; i < sizeof(float); i++) + { floatBytes.b[i] = readByte(address + i); } return floatBytes.f; } // Write an array of doubles to the EEPROM -void writeDoubles(unsigned int baseAddress, double *data, int count) { - for (int i = 0; i < count; i++) { +void writeDoubles(unsigned int baseAddress, double *data, int count) +{ + for (int i = 0; i < count; i++) + { // Calculate the address for this double // Each double is 8 bytes unsigned int address = baseAddress + (i * sizeof(double)); @@ -1627,8 +1867,10 @@ } // Read an array of doubles from the EEPROM -void readDoubles(unsigned int baseAddress, double *data, int count) { - for (int i = 0; i < count; i++) { +void readDoubles(unsigned int baseAddress, double *data, int count) +{ + for (int i = 0; i < count; i++) + { // Calculate the address for this double // Each double is 8 bytes unsigned int address = baseAddress + (i * sizeof(double)); @@ -1639,42 +1881,50 @@ } // Write a single double to the EEPROM -void writeDouble(unsigned int address, double data) { +void writeDouble(unsigned int address, double data) +{ // Create a union to access the bytes of the double - union { + union + { double d; byte b[8]; } doubleBytes; doubleBytes.d = data; // Write each byte of the double - for (uint i = 0; i < sizeof(double); i++) { + for (uint i = 0; i < sizeof(double); i++) + { writeByte(address + i, doubleBytes.b[i]); - delay(5); // Small delay between byte writes + delay(5); // Small delay between byte writes } } // Read a single double from the EEPROM -double readDouble(unsigned int address) { +double readDouble(unsigned int address) +{ // Create a union to build the double from bytes - union { + union + { double d; byte b[8]; } doubleBytes; // Read each byte of the double - for (uint i = 0; i < sizeof(double); i++) { + for (uint i = 0; i < sizeof(double); i++) + { doubleBytes.b[i] = readByte(address + i); } return doubleBytes.d; } // Write a single byte to the EEPROM -void writeByte(unsigned int address, byte data) { +void writeByte(unsigned int address, byte data) +{ // Check if address is valid - if (address >= EEPROM_SIZE) { + if (address >= EEPROM_SIZE) + { Serial.print("Error: Address out of range: "); Serial.println(address); return; @@ -1690,9 +1940,10 @@ SPI.transfer(EEPROM_WRITE); // Send address - if (EEPROM_SIZE > 256) { + if (EEPROM_SIZE > 256) + { // For AT25040B (4Kbit), use 9-bit address (A8-A0) - SPI.transfer((address >> 8) & 0x01); // Only 1 bit for A8 + SPI.transfer((address >> 8) & 0x01); // Only 1 bit for A8 } SPI.transfer(address & 0xFF); @@ -1703,13 +1954,15 @@ digitalWrite(TEENSY_SPI_CS_PIN, LOW); // Wait for the write to complete - delay(5); // 5ms max write time according to datasheet + delay(5); // 5ms max write time according to datasheet } // Read a single byte from the EEPROM -byte readByte(unsigned int address) { +byte readByte(unsigned int address) +{ // Check if address is valid - if (address >= EEPROM_SIZE) { + if (address >= EEPROM_SIZE) + { Serial.print("Error: Address out of range: "); Serial.println(address); return 0; @@ -1724,9 +1977,10 @@ SPI.transfer(EEPROM_READ); // Send address - if (EEPROM_SIZE > 256) { + if (EEPROM_SIZE > 256) + { // For AT25040B (4Kbit), use 9-bit address (A8-A0) - SPI.transfer((address >> 8) & 0x01); // Only 1 bit for A8 + SPI.transfer((address >> 8) & 0x01); // Only 1 bit for A8 } SPI.transfer(address & 0xFF); @@ -1739,18 +1993,21 @@ return data; } -void writeEnable() { +void writeEnable() +{ digitalWrite(TEENSY_SPI_CS_PIN, HIGH); SPI.transfer(EEPROM_WREN); digitalWrite(TEENSY_SPI_CS_PIN, LOW); - delay(1); // Small delay to ensure the command is processed + delay(1); // Small delay to ensure the command is processed } // Modified command handler for saving mixed precision values to EEPROM // Format: "save,value1,value2,value3,..." (first DOUBLE_COUNT are doubles, rest are floats) -void handleSaveCommand(String command) { +void handleSaveCommand(String command) +{ int firstComma = command.indexOf(','); - if (firstComma < 0) { + if (firstComma < 0) + { Serial.println("Error: Invalid save command format"); return; } @@ -1766,24 +2023,32 @@ int valueIndex = 0; // Parse values - while (valueIndex < maxTotal && dataStr.length() > 0) { + while (valueIndex < maxTotal && dataStr.length() > 0) + { int commaPos = dataStr.indexOf(','); String valueStr; - if (commaPos >= 0) { + if (commaPos >= 0) + { valueStr = dataStr.substring(0, commaPos); dataStr = dataStr.substring(commaPos + 1); - } else { + } + else + { // Last value valueStr = dataStr; - dataStr = ""; // Clear dataStr to end the loop + dataStr = ""; // Clear dataStr to end the loop } // Convert string and store in appropriate array - if (valueStr.length() > 0) { - if (valueIndex < DOUBLE_COUNT) { + if (valueStr.length() > 0) + { + if (valueIndex < DOUBLE_COUNT) + { // Store as double doubleValues[valueIndex] = strtod(valueStr.c_str(), NULL); - } else { + } + else + { // Store as float floatValues[valueIndex - DOUBLE_COUNT] = strtof(valueStr.c_str(), NULL); } @@ -1792,11 +2057,13 @@ } // Check if we got at least 1 value - if (valueIndex == 0) { + if (valueIndex == 0) + { Serial.println("Error: No valid values found"); return; } - if (valueIndex > maxTotal) { + if (valueIndex > maxTotal) + { Serial.print("Error: Too many values, maximum is "); Serial.print(maxTotal); Serial.print(" ("); @@ -1820,39 +2087,46 @@ Serial.println(" floats):"); // Display double values - for (int i = 0; i < doubleCount; i++) { + for (int i = 0; i < doubleCount; i++) + { Serial.print(i); Serial.print(" (double): "); Serial.println(doubleValues[i], 10); } // Display float values - for (int i = 0; i < floatCount; i++) { + for (int i = 0; i < floatCount; i++) + { Serial.print(i + DOUBLE_COUNT); Serial.print(" (float): "); - Serial.println(floatValues[i], 6); // Floats typically have 6-7 significant digits + Serial.println(floatValues[i], 6); // Floats typically have 6-7 significant digits } Serial.println("Confirm write to EEPROM? (y/n)"); // Wait for user confirmation unsigned long startTime = millis(); - const unsigned long timeout = 30000; // 30 seconds timeout - while (true) { + const unsigned long timeout = 30000; // 30 seconds timeout + while (true) + { // Check for timeout - if (millis() - startTime > timeout) { + if (millis() - startTime > timeout) + { Serial.println("Timed out waiting for confirmation. Operation aborted."); return; } // Check if data is available to read - if (Serial.available() > 0) { + if (Serial.available() > 0) + { char response = Serial.read(); // Clear any remaining characters in buffer (like newline) - while (Serial.available() > 0) { + while (Serial.available() > 0) + { Serial.read(); } // Process response - if (response == 'y' || response == 'Y') { + if (response == 'y' || response == 'Y') + { // User confirmed, write to EEPROM unsigned int baseAddress = 0; writeMixedValues(baseAddress, doubleValues, doubleCount, floatValues, floatCount); @@ -1867,11 +2141,13 @@ bool verificationSuccess = true; // Verify doubles - for (int i = 0; i < doubleCount; i++) { + for (int i = 0; i < doubleCount; i++) + { Serial.print(i); Serial.print(" (double): "); Serial.println(readDoubles[i], 10); - if (abs(doubleValues[i] - readDoubles[i]) > 0.000000001) { + if (abs(doubleValues[i] - readDoubles[i]) > 0.000000001) + { verificationSuccess = false; Serial.print("Warning: Double value "); Serial.print(i); @@ -1880,29 +2156,38 @@ } // Verify floats - for (int i = 0; i < floatCount; i++) { + for (int i = 0; i < floatCount; i++) + { Serial.print(i + DOUBLE_COUNT); Serial.print(" (float): "); Serial.println(readFloats[i], 6); - if (abs(floatValues[i] - readFloats[i]) > 0.000001) { + if (abs(floatValues[i] - readFloats[i]) > 0.000001) + { verificationSuccess = false; Serial.print("Warning: Float value "); Serial.print(i + DOUBLE_COUNT); Serial.println(" does not match exactly what was written"); } } - if (verificationSuccess) { + if (verificationSuccess) + { Serial.println("Verification successful: All values match!"); - } else { + } + else + { Serial.println("Verification Failed, confirm device attached and powered."); } return; - } else if (response == 'n' || response == 'N') { + } + else if (response == 'n' || response == 'N') + { // User aborted Serial.println("Operation aborted by user."); return; - } else { + } + else + { // Invalid response, ask again Serial.println("Please enter 'y' to confirm or 'n' to abort:"); } @@ -1912,8 +2197,9 @@ } } -void handleLoadCommand() { - unsigned int baseAddress = 0; // Same address used in save +void handleLoadCommand() +{ + unsigned int baseAddress = 0; // Same address used in save // Calculate maximum values we can store int maxFloats = (128 - (DOUBLE_COUNT * 8)) / 4; @@ -1926,25 +2212,29 @@ Serial.println("Loaded values from EEPROM:"); // Display doubles - for (int i = 0; i < DOUBLE_COUNT; i++) { + for (int i = 0; i < DOUBLE_COUNT; i++) + { Serial.print(i); Serial.print(" (double): "); Serial.println(doubleValues[i], 10); } // Display floats - for (int i = 0; i < maxFloats; i++) { + for (int i = 0; i < maxFloats; i++) + { Serial.print(i + DOUBLE_COUNT); Serial.print(" (float): "); Serial.println(floatValues[i], 6); } } // Function to initialize all units sequentially -void initializeAllUnits() { +void initializeAllUnits() +{ Serial.println("Initializing all units (1-6)..."); - for (int unit = 1; unit <= 6; unit++) { + for (int unit = 1; unit <= 6; unit++) + { Serial.print("Initializing Unit "); Serial.println(unit); @@ -1968,23 +2258,12 @@ Serial.println("All units initialized successfully"); } -// Enum to track unit status -enum UnitStatus { - STATUS_SKIPPED = 0, // Unit was never attempted to be initialized - STATUS_FAILED = 1, // Unit was attempted but failed initialization - STATUS_SUCCESS = 2 // Unit was successfully initialized -}; - -// Array to keep track of unit status (indexed 0-5 for units 1-6) -// Initialize all to skipped by default -UnitStatus unitStatus[6] = { - STATUS_SKIPPED, STATUS_SKIPPED, STATUS_SKIPPED, - STATUS_SKIPPED, STATUS_SKIPPED, STATUS_SKIPPED -}; - // Function to ask the user which units to initialize and then initialize them -void initializeSelectedUnits() { - Serial.println("\nUnit Initialization"); +bool initializeSelectedUnits() +{ + initStatus = INIT_STATUS_IN_PROGRESS; + Serial.write((uint8_t *)&initStatus, sizeof(initStatus)); + // Serial.println("\nUnit Initialization"); // Serial.println("Enter 'all' to initialize all units (1-6)"); // Serial.println("Or enter specific units separated by commas (e.g., '1,3,5')"); // Serial.println("Then press Enter:"); @@ -1997,20 +2276,19 @@ // Read the input String input = Serial.readStringUntil('\n'); - input.trim(); // Remove any whitespace + input.trim(); // Remove any whitespace - // Initialize all units - Serial.println("Initializing all units (1-6)..."); + // Serial.println("Initializing all units (1-6)..."); - for (int unit = 1; unit <= 6; unit++) { + for (int unit = 1; unit <= 6; unit++) + { initializeSingleUnit(unit); } - + isInitialized = true; // Print summary of unit status - printUnitStatusSummary(); + // printUnitStatusSummary(); - // else { // // Parse the comma-separated list // Serial.print("Initializing selected units: "); @@ -2054,30 +2332,35 @@ // Serial.println("\nSelected units initialization complete"); // Print summary of unit status + return isInitialized; } // Function to select the first working unit -void selectFirstWorkingUnit() { - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_SUCCESS) { +void selectFirstWorkingUnit() +{ + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SUCCESS) + { // Found a working unit, select it (adding 1 because units are 1-indexed) int workingUnit = i + 1; selectUnit(workingUnit); - Serial.print("\nAutomatically selected first working unit: Unit "); - Serial.println(workingUnit); + // Serial.print("\nAutomatically selected first working unit: Unit "); + // Serial.println(workingUnit); startupAD5941(); - return; // Exit after selecting the first working unit + return; // Exit after selecting the first working unit } } // If we get here, no working units were found - Serial.println("\nNo working units available to select!"); + // Serial.println("\nNo working units available to select!"); } // Helper function to initialize a single unit -void initializeSingleUnit(int unit) { - Serial.print("\nInitializing Unit "); - Serial.println(unit); +void initializeSingleUnit(int unit) +{ + // Serial.print("\nInitializing Unit "); + // Serial.println(unit); // Select the unit selectUnit(unit); @@ -2086,38 +2369,48 @@ delay(50); // Initialize the AD5940 analog front-end for this unit - if (startupAD5941()) { + if (startupAD5941()) + { // Startup failed - Serial.print("Unit "); - Serial.print(unit); - Serial.println(" startup FAILED"); + // Serial.print("Unit "); + // Serial.print(unit); + // Serial.println(" startup FAILED"); // Record the failure in our status array (0-indexed) unitStatus[unit - 1] = STATUS_FAILED; - } else { + } + else + { // Startup worked - Serial.print("Unit "); - Serial.print(unit); - Serial.println(" startup SUCCESS"); + // Serial.print("Unit "); + // Serial.print(unit); + // Serial.println(" startup SUCCESS"); // Record the success in our status array unitStatus[unit - 1] = STATUS_SUCCESS; } } // Function to print a summary of the status of all units -void printUnitStatusSummary() { +void printUnitStatusSummary() +{ // Count units by status int skippedCount = 0; int failedCount = 0; int successCount = 0; - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_SKIPPED) { + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SKIPPED) + { skippedCount++; - } else if (unitStatus[i] == STATUS_FAILED) { + } + else if (unitStatus[i] == STATUS_FAILED) + { failedCount++; - } else { // STATUS_SUCCESS + } + else + { // STATUS_SUCCESS successCount++; } } @@ -2134,12 +2427,16 @@ Serial.println(skippedCount); // List failed units - if (failedCount > 0) { + if (failedCount > 0) + { Serial.print("Failed units: "); bool firstPrinted = false; - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_FAILED) { - if (firstPrinted) { + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_FAILED) + { + if (firstPrinted) + { Serial.print(", "); } Serial.print(i + 1); @@ -2150,12 +2447,16 @@ } // List skipped units - if (skippedCount > 0) { + if (skippedCount > 0) + { Serial.print("Skipped units: "); bool firstPrinted = false; - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_SKIPPED) { - if (firstPrinted) { + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SKIPPED) + { + if (firstPrinted) + { Serial.print(", "); } Serial.print(i + 1); @@ -2167,15 +2468,21 @@ // Report available units Serial.print("\nAvailable units: "); - if (successCount == 0) { + if (successCount == 0) + { Serial.println("NONE - No successfully initialized units!"); - } else { + } + else + { // List available units bool firstPrinted = false; - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_SUCCESS) { + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SUCCESS) + { // Add comma if not the first available unit - if (firstPrinted) { + if (firstPrinted) + { Serial.print(", "); } Serial.print(i + 1); @@ -2189,18 +2496,22 @@ } // Function to measure all successfully initialized units -void measureAllSuccessfulUnits() { +void measureAllSuccessfulUnits() +{ int successCount = 0; // Count successful units first - for (int i = 0; i < 6; i++) { - if (unitStatus[i] == STATUS_SUCCESS) { + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SUCCESS) + { successCount++; } } // If no successful units, report and return - if (successCount == 0) { + if (successCount == 0) + { Serial.println("No successfully initialized units to measure!"); return; } @@ -2211,15 +2522,18 @@ AppIMPCfg_Type *pImpedanceCfg; // Loop through and measure each successful unit - for (int unit = 1; unit <= 6; unit++) { + for (int unit = 1; unit <= 6; unit++) + { // Skip units that aren't successfully initialized - if (unitStatus[unit - 1] != STATUS_SUCCESS) { + if (unitStatus[unit - 1] != STATUS_SUCCESS) + { continue; } // Select the unit selectUnit(unit); - if (propagateSettingsChanges) { + if (propagateSettingsChanges) + { AppIMPGetCfg(&pImpedanceCfg); pImpedanceCfg->bParaChanged = bTRUE; } @@ -2233,49 +2547,58 @@ // quick patch for fixing spontaneous measurements unsigned long currentMillis = millis(); - if (currentMillis - previousMillis >= jitterTimeout) { + if (currentMillis - previousMillis >= jitterTimeout) + { Serial.println("Performing settling measurements..."); // Run multiple settling measurements in a loop - for (int settling = 0; settling < numSettlingMeasurements; settling++) { + for (int settling = 0; settling < numSettlingMeasurements; settling++) + { // Run one complete measurement cycle but discard results AppIMPInit(AppBuff, APPBUFF_SIZE); AppIMPCtrl(IMPCTRL_START, 0); // Wait for completion (simplified polling) int timeout_count = 0; - while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) { + while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) + { delay(1); timeout_count++; } - if (AD5940_GetMCUIntFlag() == 1) { + if (AD5940_GetMCUIntFlag() == 1) + { AD5940_ClrMCUIntFlag(); uint32_t dummy_size = APPBUFF_SIZE; - AppIMPISR(AppBuff, &dummy_size); // Discard this data + AppIMPISR(AppBuff, &dummy_size); // Discard this data } AppIMPCtrl(IMPCTRL_STOPSYNC, 0); // Small delay between settling measurements - if (settling < numSettlingMeasurements - 1) { - AD5940_Delay10us(1000); // 10ms between measurements + if (settling < numSettlingMeasurements - 1) + { + AD5940_Delay10us(1000); // 10ms between measurements } } } // Perform impedance measurement - for (int i = 0; i < repeatNumber; i++) { + for (int i = 0; i < repeatNumber; i++) + { AppIMPMeasure(i); } float medianMag; float medianPhase; // take median and output results - if (repeatNumber == 1) { + if (repeatNumber == 1) + { medianMag = magnitudeArray[0]; medianPhase = phaseArray[0]; - } else { + } + else + { medianMag = calculateMedian(magnitudeArray, repeatNumber); medianPhase = calculateMedian(phaseArray, repeatNumber); } @@ -2305,17 +2628,22 @@ } -float calculateMedian(float arr[], int size) { +float calculateMedian(float arr[], int size) +{ // Create a temporary copy of the array float tempArr[size]; - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { tempArr[i] = arr[i]; } // Sort the temporary array - for (int i = 0; i < size - 1; i++) { - for (int j = 0; j < size - i - 1; j++) { - if (tempArr[j] > tempArr[j + 1]) { + for (int i = 0; i < size - 1; i++) + { + for (int j = 0; j < size - i - 1; j++) + { + if (tempArr[j] > tempArr[j + 1]) + { float temp = tempArr[j]; tempArr[j] = tempArr[j + 1]; tempArr[j + 1] = temp; @@ -2324,41 +2652,682 @@ } // Return median value - if (size % 2 == 0) { + if (size % 2 == 0) + { return (tempArr[size / 2 - 1] + tempArr[size / 2]) / 2.0; - } else { + } + else + { return tempArr[size / 2]; } } -static void AD5940_EnableAdcMaxSaturationIRQ(uint32_t max_code, uint16_t hysteresis) { +static void AD5940_EnableAdcMaxSaturationIRQ(uint32_t max_code, uint16_t hysteresis) +{ // 1) Program the max comparator + hysteresis - AD5940_WriteReg(0x000020B0, max_code); // ADCMAX - AD5940_WriteReg(0x000020B4, hysteresis); // ADCMAXSMEN + AD5940_WriteReg(0x000020B0, max_code); // ADCMAX + AD5940_WriteReg(0x000020B4, hysteresis); // ADCMAXSMEN // 2) Enable FLAG5 in INTC1 (had to move INTC1 off debug, but we weren't really using it -MK) - uint32_t sel1 = AD5940_ReadReg(0x0000300C); // INTCSEL1 - sel1 |= (1u << 5); // INTSEL5: enable ADCMAX fail IRQ on this controller + uint32_t sel1 = AD5940_ReadReg(0x0000300C); // INTCSEL1 + sel1 |= (1u << 5); // INTSEL5: enable ADCMAX fail IRQ on this controller AD5940_WriteReg(0x0000300C, sel1); // 3) Clear any stale flags (write-1-to-clear) - AD5940_WriteReg(0x00003004, (1u << 5)); // INTCCLR: clear ADCMAX fail - if (verboseMode) { + AD5940_WriteReg(0x00003004, (1u << 5)); // INTCCLR: clear ADCMAX fail + if (verboseMode) + { Serial.println("saturationFlagSet"); } } -static inline bool AD5940_IsAdcMaxSaturatedAndClear() { +static inline bool AD5940_IsAdcMaxSaturatedAndClear() +{ // Read flags (either INTCFLAG0 or INTCFLAG1 is fine since we enabled in INTC1) - uint32_t f1 = AD5940_ReadReg(0x00003014); // INTCFLAG1 - bool saturated = (f1 & (1u << 5)) != 0; // FLAG5: ADC max fail + uint32_t f1 = AD5940_ReadReg(0x00003014); // INTCFLAG1 + bool saturated = (f1 & (1u << 5)) != 0; // FLAG5: ADC max fail - if (saturated) { + if (saturated) + { // Clear it (write-1-to-clear) - AD5940_WriteReg(0x00003004, (1u << 5)); // INTCCLR: clear ADCMAX fail + AD5940_WriteReg(0x00003004, (1u << 5)); // INTCCLR: clear ADCMAX fail } return saturated; } -#endif /* ad5940_library_extension_C */ \ No newline at end of file +// ************************************ Diality ********************************************** +// Function to measure all successfully initialized units +bool getAllMesaurements(void) +{ + int successCount = 0; + bool retVal = true; // Success + + // Count successful units first + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SUCCESS) + { + successCount++; + } + } + + // If no successful units, report and return + if (successCount == 0) + { + retVal = false; // Error + // Serial.println("No successfully initialized units to measure!"); + return retVal; + } + + AppIMPCfg_Type *pImpedanceCfg; + + // Loop through and measure each successful unit + for (int unit = 1; unit <= 6; unit++) + { + // Skip units that aren't successfully initialized + if (unitStatus[unit - 1] != STATUS_SUCCESS) + { + continue; + } + + // Select the unit + selectUnit(unit); + if (propagateSettingsChanges) + { + AppIMPGetCfg(&pImpedanceCfg); + pImpedanceCfg->bParaChanged = bTRUE; + } + // Allow some settling time after switching units + delay(50); + + // quick patch for fixing spontaneous measurements + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= jitterTimeout) + { + // Run multiple settling measurements in a loop + for (int settling = 0; settling < numSettlingMeasurements; settling++) + { + // Run one complete measurement cycle but discard results + AppIMPInit(AppBuff, APPBUFF_SIZE); + AppIMPCtrl(IMPCTRL_START, 0); + + // Wait for completion (simplified polling) + int timeout_count = 0; + while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) + { + delay(1); + timeout_count++; + } + + if (AD5940_GetMCUIntFlag() == 1) + { + AD5940_ClrMCUIntFlag(); + uint32_t dummy_size = APPBUFF_SIZE; + AppIMPISR(AppBuff, &dummy_size); // Discard this data + } + + AppIMPCtrl(IMPCTRL_STOPSYNC, 0); + + // Small delay between settling measurements + if (settling < numSettlingMeasurements - 1) + { + AD5940_Delay10us(1000); // 10ms between measurements + } + } + } + + // Perform impedance measurement + for (int i = 0; i < repeatNumber; i++) + { + AppIMPMeasure(i); + } + float medianMag; + float medianPhase; + + // take median and output results + if (repeatNumber == 1) + { + medianMag = magnitudeArray[0]; + medianPhase = phaseArray[0]; + } + else + { + medianMag = calculateMedian(magnitudeArray, repeatNumber); + medianPhase = calculateMedian(phaseArray, repeatNumber); + } + + // Perform RTD (temperature) measurement + float rtd_res = getRTDMeasurements(rtdVoltageValue); + + previousMillis = millis(); + + // Check for "ERROR: AD5940 wakeup timeout" + if(-1000.0f == rtd_res) + { + // In case of an error, send all 0's + initPacketToDefault(unit); + } + if(false == isSensorInitialized(unit)) + { + // In case the unit is not initialized, send all 0's + initPacketToDefault(unit); + } + else + { + // Pack the sensor data + // The ALY code loops from 1 to 6. Our packet index from 0 to 5. So, index at i-1. + int sensorIdx = unit - 1; + sensorPacket[sensorIdx].sensorNum = unit; // Conductivity Sensor number i.e unit 1 to 6. + sensorPacket[sensorIdx].impFreq = storedFrequency; // Impedance Frequency + sensorPacket[sensorIdx].impDataPoints = 1; // Impedance Data Points. Hard coded to 1 + sensorPacket[sensorIdx].impRzMag = medianMag; // Value of medianMag + sensorPacket[sensorIdx].impRzPhase = medianPhase; // Value of medianPhase + sensorPacket[sensorIdx].rtdFreq = 0; // RTD Frequency. Hard coded to 0.0 + sensorPacket[sensorIdx].rtdDataPoints = 1; // Impedance Data Points. Hard coded to 1 + sensorPacket[sensorIdx].rtdRzMag = rtd_res; // Value of rtd_resistance + sensorPacket[sensorIdx].rtdRzPhase = 0.0; // RTD Rz Phase. Hard coded to 0.0 + } + } + // Send all the sensor data to DD + Serial.write((uint8_t *)&sensorPacket, sizeof(sensorPacket)); + + propagateSettingsChanges = 0; + return retVal; +} + +void getSelectedSensorMeasurements(void) +{ + // quick patch for fixing spontaneous measurements + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= jitterTimeout) + { + + // Run multiple settling measurements in a loop + for (int settling = 0; settling < numSettlingMeasurements; settling++) + { + if (settling == 0) + { + // Serial.println("Performing settling measurements..."); + } + if (verboseMode) + { + // Serial.printf(" Settling Measurement#: %u ", settling); + // Serial.println(" "); + } + // Run one complete measurement cycle but discard results + AppIMPInit(AppBuff, APPBUFF_SIZE); + AppIMPCtrl(IMPCTRL_START, 0); + + // Wait for completion (simplified polling) + int timeout_count = 0; + while (AD5940_GetMCUIntFlag() != 1 && timeout_count < 1000) + { + delay(1); + timeout_count++; + } + + if (AD5940_GetMCUIntFlag() == 1) + { + AD5940_ClrMCUIntFlag(); + uint32_t dummy_size = APPBUFF_SIZE; + AppIMPISR(AppBuff, &dummy_size); // Discard this data + } + + AppIMPCtrl(IMPCTRL_STOPSYNC, 0); + + // Small delay between settling measurements + if (settling < numSettlingMeasurements - 1) + { + AD5940_Delay10us(1000); // 10ms between measurements + } + } + if (verboseMode) + { + // Serial.println(" End Settling Measurements"); + } + } + + if (verboseMode) + { + // Serial.println("Conductivity"); + // Serial.print(" Conductivity TIA Resistor: "); + switch (DEFAULT_RTIA) + { + case HSTIARTIA_200: + // Serial.println("200 Ohm"); + break; + case HSTIARTIA_1K: + // Serial.println("1K Ohm"); + break; + case HSTIARTIA_5K: + // Serial.println("5K Ohm"); + break; + case HSTIARTIA_10K: + // Serial.println("10K Ohm"); + break; + case HSTIARTIA_20K: + // Serial.println("20K Ohm"); + break; + case HSTIARTIA_40K: + // Serial.println("40K Ohm"); + break; + case HSTIARTIA_80K: + // Serial.println("80K Ohm"); + break; + case HSTIARTIA_160K: + // Serial.println("160K Ohm"); + break; + default: + // Serial.println("Unknown"); + break; + } + } + // Perform impedance measurement + for (int i = 0; i < repeatNumber; i++) + { + AppIMPMeasure(i); + } + if (verboseMode) + { + if (AD5940_IsAdcMaxSaturatedAndClear()) + { + // Serial.println("SaturationFlag tripped!"); + } + else + { + // Serial.println("SaturationFlag NOT tripped!"); + } + } + float medianMag; + float medianPhase; + // take median and output results + if (repeatNumber == 1) + { + medianMag = magnitudeArray[0]; + medianPhase = phaseArray[0]; + } + else + { + medianMag = calculateMedian(magnitudeArray, repeatNumber); + medianPhase = calculateMedian(phaseArray, repeatNumber); + } + + // Serial.printf("Freq: %.2f Hz ", storedFrequency); + + // Print the number of data points + // Serial.printf("DataPoints: %lu ", 1); + + // Print magnitude in ohms and phase in degrees + // Note: Converting phase from radians to degrees (phase * 180 / π) + // Serial.printf("RzMag: %.2f Ohm, RzPhase: %.2f deg", + // medianMag, + // medianPhase); + + // Add separator and perform RTD (temperature) measurement + // Serial.print(";"); + float rtd_res = getRTDMeasurements(rtdVoltageValue); + // Serial.print("*"); + previousMillis = millis(); + + // Check for "ERROR: AD5940 wakeup timeout" + if(-1000.0f == rtd_res) + { + // In case of an error, send all 0's + initPacketToDefault(singleSensorPacket.sensorNum); + } + else + { + singleSensorPacket.impFreq = storedFrequency; // Impedance Frequency + singleSensorPacket.impDataPoints = 1; // Impedance Data Points. Hard coded to 1 + singleSensorPacket.impRzMag = medianMag; // Value of medianMag + singleSensorPacket.impRzPhase = medianPhase; // Value of medianPhase + singleSensorPacket.rtdFreq = 0; // RTD Frequency. Hard coded to 0.0 + singleSensorPacket.rtdDataPoints = 1; // Impedance Data Points. Hard coded to 1 + singleSensorPacket.rtdRzMag = rtd_res; // Value of rtd_resistance + singleSensorPacket.rtdRzPhase = 0.0; // RTD Rz Phase. Hard coded to 0.0 + } + Serial.write((uint8_t *)&singleSensorPacket, sizeof(singleSensorPacket)); + } + +float getRTDMeasurements(float sensor_mV) +{ + AFERefCfg_Type aferef_cfg; + HSLoopCfg_Type HsLoopCfg; + DSPCfg_Type dsp_cfg; + + uint32_t adcCode_rtd, adcCode_ref; + float volt_rtd, volt_ref; + float rtd_resistance; + const float RtdRefRes = REF_RESISTOR_VALUE; + + // Wake up the AD5940 from low power mode + if (AD5940_WakeUp(10) > 10) + { + // Serial.println("ERROR: AD5940 wakeup timeout"); + return -1000.0f; + } + + // Reset LPDAC configuration before RTD measurement + LPDACCfg_Type lpdac_reset = {0}; + lpdac_reset.PowerEn = bFALSE; + AD5940_LPDACCfgS(&lpdac_reset); + + // Configure High-Speed Loop + HsLoopCfg.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2; + HsLoopCfg.HsDacCfg.HsDacGain = HSDACGAIN_1; + HsLoopCfg.HsDacCfg.HsDacUpdateRate = 7; + + // TIA settings + HsLoopCfg.HsTiaCfg.DiodeClose = bFALSE; + HsLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1; + HsLoopCfg.HsTiaCfg.HstiaCtia = 32; + HsLoopCfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN; + HsLoopCfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN; + HsLoopCfg.HsTiaCfg.HstiaRtiaSel = RTD_RTIA; + + // Switch matrix for RTD measurement + HsLoopCfg.SWMatCfg.Dswitch = RTD_DSWITCH; + HsLoopCfg.SWMatCfg.Pswitch = RTD_PSWITCH; + HsLoopCfg.SWMatCfg.Nswitch = RTD_NSWITCH; + HsLoopCfg.SWMatCfg.Tswitch = RTD_TSWITCH; + + // Configure waveform generator + HsLoopCfg.WgCfg.WgType = WGTYPE_MMR; + HsLoopCfg.WgCfg.GainCalEn = bFALSE; + HsLoopCfg.WgCfg.OffsetCalEn = bFALSE; + uint32_t code = mV_to_WgCode(sensor_mV); + HsLoopCfg.WgCfg.WgCode = code; + + // Apply configuration twice (didn't seem to stick unless we did) + AD5940_HSLoopCfgS(&HsLoopCfg); + AD5940_HSLoopCfgS(&HsLoopCfg); + + // Configure ADC + dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N; + dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P; + dsp_cfg.ADCBaseCfg.ADCPga = ADCPGA_1; + + dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16; + dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ; + dsp_cfg.ADCFilterCfg.ADCSinc2Osr = ADCSINC2OSR_44; + dsp_cfg.ADCFilterCfg.ADCSinc3Osr = ADCSINC3OSR_4; + dsp_cfg.ADCFilterCfg.BpNotch = bTRUE; + dsp_cfg.ADCFilterCfg.BpSinc3 = bTRUE; + dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE; + + AD5940_DSPCfgS(&dsp_cfg); + + // Enable AFE blocks + AD5940_AFECtrlS(AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR | AFECTRL_EXTBUFPWR | AFECTRL_WG | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR | AFECTRL_SINC2NOTCH | AFECTRL_ADCPWR, + bTRUE); + + // Restart waveform generator with proper sequence + AD5940_AFECtrlS(AFECTRL_WG, bFALSE); + AD5940_Delay10us(1000); // 10ms settling + AD5940_AFECtrlS(AFECTRL_WG, bTRUE); + + // Extended settling time for RTD measurement + AD5940_Delay10us(500); // 5ms settling time + + // Sample RTD with improved averaging + sampleADC(1); + uint64_t accRTD = 0; + for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) + { + accRTD += sampleADC(1); // Increased sampling delay + + AD5940_Delay10us(10); // Small delay between samples + } + adcCode_rtd = accRTD / NUM_RTD_SAMPLES; + + // Reconfigure switch matrix for reference resistor + HsLoopCfg.SWMatCfg.Dswitch = SWD_RCAL0; + HsLoopCfg.SWMatCfg.Pswitch = SWP_RCAL0; + HsLoopCfg.SWMatCfg.Nswitch = SWN_RCAL1; + HsLoopCfg.SWMatCfg.Tswitch = SWT_RCAL1 | SWT_TRTIA; + + // Apply new switch configuration + AD5940_SWMatrixCfgS(&HsLoopCfg.SWMatCfg); + + // Additional settling time after switch change + AD5940_Delay10us(500); // 5ms settling + sampleADC(1); + // Sample reference resistor + uint64_t accREF = 0; + for (uint32_t i = 0; i < NUM_RTD_SAMPLES; ++i) + { + accREF += sampleADC(1); + AD5940_Delay10us(10); + } + adcCode_ref = accREF / NUM_RTD_SAMPLES; + + // reset switch matrix + resetSwitchMatrix(); + + // Disable all AFE blocks that were enabled + AD5940_AFECtrlS(AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR | AFECTRL_EXTBUFPWR | AFECTRL_WG | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR | AFECTRL_SINC2NOTCH | AFECTRL_ADCPWR, bFALSE); + + // Convert to voltages + volt_ref = AD5940_ADCCode2Volt(adcCode_ref, ADCPGA_1, 1.82); + volt_rtd = AD5940_ADCCode2Volt(adcCode_rtd, ADCPGA_1, 1.82); + + // Calculate resistance + rtd_resistance = RtdRefRes * (volt_ref / volt_rtd); + + // Verbose output + if (verboseMode) + { + Serial.println(" "); + + // Serial.println("RTD Measurement"); + // Serial.print(" RCal Averaged Measurement: "); + Serial.println(adcCode_ref); + // Serial.print(" RTD Averaged Measurement: "); + Serial.println(adcCode_rtd); + // Serial.printf(" NUM_SAMPLES: %u", NUM_RTD_SAMPLES); + Serial.println(" "); + // Serial.print(" RTD TIA Resistor: "); + switch (RTD_RTIA) + { + case HSTIARTIA_200: + // Serial.println("200 Ohm"); + break; + case HSTIARTIA_1K: + // Serial.println("1K Ohm"); + break; + case HSTIARTIA_5K: + // Serial.println("5K Ohm"); + break; + case HSTIARTIA_10K: + // Serial.println("10K Ohm"); + break; + case HSTIARTIA_20K: + // Serial.println("20K Ohm"); + break; + case HSTIARTIA_40K: + // Serial.println("40K Ohm"); + break; + case HSTIARTIA_80K: + // Serial.println("80K Ohm"); + break; + case HSTIARTIA_160K: + // Serial.println("160K Ohm"); + break; + default: + // Serial.println("Unknown"); + break; + } + } + return rtd_resistance; +} + +void getEEPROMdata(void) +{ + unsigned int baseAddress = 0; // Same address used in save + + // Calculate maximum values we can store + int maxFloats = (128 - (DOUBLE_COUNT * 8)) / 4; + + double doubleValues[DOUBLE_COUNT]; + float floatValues[maxFloats]; + + readMixedValues(baseAddress, doubleValues, DOUBLE_COUNT, floatValues, maxFloats); + + // Display doubles + for (int i = 0; i < DOUBLE_COUNT; i++) + { + eepromDataPacket.doubleValue[i] = doubleValues[i]; + } + + // Display floats + for (int i = 0; i < maxFloats; i++) + { + eepromDataPacket.floatValue[i] = floatValues[i]; + } + + Serial.write((uint8_t *)&eepromDataPacket, sizeof(eepromDataPacket)); +} + +void getMesaurementSettings(void) +{ + // Pointer to store configuration structure + AppIMPCfg_Type *cfg; + + // Get the current impedance measurement configuration + AppIMPGetCfg(&cfg); + + measurementSettingsPacket.SinFreq = cfg->SinFreq; + measurementSettingsPacket.DacVoltPP = cfg->DacVoltPP; + measurementSettingsPacket.BiasVolt = cfg->BiasVolt; + measurementSettingsPacket.HstiaRtiaSel = cfg->HstiaRtiaSel; + measurementSettingsPacket.AdcPgaGain = cfg->AdcPgaGain; + measurementSettingsPacket.DftNum = cfg->DftNum; + measurementSettingsPacket.ADCAvgNum = cfg->ADCAvgNum; + Serial.write((uint8_t *)&measurementSettingsPacket, sizeof(measurementSettingsPacket)); +} + +void getInitStatus(void) +{ + // Count units by status + int skippedCount = 0; + int failedCount = 0; + int successCount = 0; + + // If Initialization function has been completely executed + if(true == isInitialized) + { + // Count number of skipped, failed and successful sensor initialization + for (int i = 0; i < 6; i++) + { + if (unitStatus[i] == STATUS_SKIPPED) + { + skippedCount++; + } + else if (unitStatus[i] == STATUS_FAILED) + { + failedCount++; + } + else + { // STATUS_SUCCESS + successCount++; + } + } + + // If any of the sensor was skipped or failed, then + if ((skippedCount > 0) || (failedCount > 0)) + { + // Set init status to failed + initStatus = INIT_STATUS_FAILED; + } + // If all the sensors were initialized successfully, then + else if(successCount == MAX_NUM_OF_SENSORS) + { + // Set the init status to initialized. + initStatus = INIT_STATUS_INITIALIZED; + } + else + { + // Still need to decide, what to do here. + } + } + + Serial.write((uint8_t *)&initStatus, sizeof(initStatus)); +} + +void sendAllSensorData( void ) +{ + int i = 0; + while(sendSensorData) + { + // Read a line from the serial port until newline character + String command = Serial.readStringUntil('\n'); + + if(command.length() > 0) + { + break; + } + else + { + // Get measurements for a + if ( true == isSensorValid( i ) ) + { + if (true == isSensorInitialized( i ) ) + { + selectUnit(i); + getSelectedSensorMeasurements(); + } + } + if ( i > 6) + { + i = 1; + } + } + i = i + 1; + } +} + +void initPacketToDefault ( int unit ) +{ + if(true == isSensorValid(unit)) + { + // Pack the sensor data + // The ALY code loops from 1 to 6. Our packet index from 0 to 5. So, index at i-1. + int sensorIdx = unit - 1; + sensorPacket[sensorIdx].sensorNum = unit; // Conductivity Sensor number i.e unit 1 to 6. + sensorPacket[sensorIdx].impFreq = 0.0; // Impedance Frequency + sensorPacket[sensorIdx].impDataPoints = 0; // Impedance Data Points. Hard coded to 1 + sensorPacket[sensorIdx].impRzMag = 0.0; // Value of medianMag + sensorPacket[sensorIdx].impRzPhase = 0.0; // Value of medianPhase + sensorPacket[sensorIdx].rtdFreq = 0.0; // RTD Frequency. Hard coded to 0.0 + sensorPacket[sensorIdx].rtdDataPoints = 0; // Impedance Data Points. Hard coded to 1 + sensorPacket[sensorIdx].rtdRzMag = 0.0; // Value of rtd_resistance + sensorPacket[sensorIdx].rtdRzPhase = 0.0; // RTD Rz Phase. Hard coded to 0.0 + } +} + +bool isSensorValid (int unit) +{ + bool retVal = false; + if ( ( unit >= 1 ) && ( unit <= 6) ) + { + retVal = true; + } + return retVal; +} + +bool isSensorInitialized (int unit) +{ + bool retVal = false; + if( true == isSensorValid (unit) ) + { + int sensorIdx = unit - 1; + if(unitStatus[sensorIdx] == STATUS_SUCCESS) + { + retVal = true; + } + } + return retVal; +} + +#endif /* ad5940_library_extension_C */