gettin gooder

This commit is contained in:
pszsh 2026-01-26 14:48:45 -08:00
parent 98bd3534ea
commit b0b0feb7b2
6 changed files with 293 additions and 171 deletions

View File

@ -1,4 +1,4 @@
// Impedance.c // File: Impedance.c
#include "ad5940.h" #include "ad5940.h"
#include <stdio.h> #include <stdio.h>
#include "string.h" #include "string.h"
@ -295,7 +295,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\ AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE); AFECTRL_SINC2NOTCH, bTRUE);
AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE); AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); // Settling // Increased settling time to 2000us (16*2000) to ensure stability
AD5940_SEQGenInsert(SEQ_WAIT(16*2000));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[0]); AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[0]);
@ -317,7 +318,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N); AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE); AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); // Increased settling time to 2000us
AD5940_SEQGenInsert(SEQ_WAIT(16*2000));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[1]); AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[1]);
@ -335,7 +337,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
AD5940_ADCMuxCfgS(ADCMUXP_AIN2, ADCMUXN_AIN3); AD5940_ADCMuxCfgS(ADCMUXP_AIN2, ADCMUXN_AIN3);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE); AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); // Increased settling time to 2000us
AD5940_SEQGenInsert(SEQ_WAIT(16*2000));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[2]); AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[2]);
@ -370,7 +373,6 @@ AD5940Err AppIMPCheckFreq(float freq)
{ {
ADCFilterCfg_Type filter_cfg; ADCFilterCfg_Type filter_cfg;
DFTCfg_Type dft_cfg; DFTCfg_Type dft_cfg;
// HSDACCfg_Type hsdac_cfg; // Removed to prevent override
uint32_t WaitClks; uint32_t WaitClks;
ClksCalInfo_Type clks_cal; ClksCalInfo_Type clks_cal;
FreqParams_Type freq_params; FreqParams_Type freq_params;
@ -379,65 +381,25 @@ AD5940Err AppIMPCheckFreq(float freq)
freq_params = AD5940_GetFreqParameters(freq); freq_params = AD5940_GetFreqParameters(freq);
// CRITICAL FIX: Removed HSDAC and HSRTIA overrides. // Only switch modes if necessary to avoid glitching the sequencer
// The configuration set in AppIMPSeqCfgGen (based on user config) should persist. if(freq < 80000)
// Overriding HSTIARTIA_5K here while RtiaVal remains 200 causes massive math errors.
if(freq < 0.51)
{ {
// hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2; if (AppIMPCfg.SysClkFreq != 16000000.0) {
// hsdac_cfg.HsDacGain = HSDACGAIN_1;
// hsdac_cfg.HsDacUpdateRate = 0x1B;
// AD5940_HSDacCfgS(&hsdac_cfg);
// AD5940_HSRTIACfgS(HSTIARTIA_40K);
filter_cfg.ADCRate = ADCRATE_800KHZ; filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6; AppIMPCfg.AdcClkFreq = 16e6;
AppIMPCfg.SysClkFreq = 16000000.0;
AD5940_HPModeEn(bFALSE); AD5940_HPModeEn(bFALSE);
} }
else if(freq < 5 )
{
// hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2;
// hsdac_cfg.HsDacGain = HSDACGAIN_1;
// hsdac_cfg.HsDacUpdateRate = 0x1B;
// AD5940_HSDacCfgS(&hsdac_cfg);
// AD5940_HSRTIACfgS(HSTIARTIA_40K);
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
AD5940_HPModeEn(bFALSE);
} }
else if(freq < 450) else
{ {
// hsdac_cfg.ExcitBufGain = AppIMPCfg.ExcitBufGain; if (AppIMPCfg.SysClkFreq != 32000000.0) {
// hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
// hsdac_cfg.HsDacUpdateRate = 0x1B;
// AD5940_HSDacCfgS(&hsdac_cfg);
// AD5940_HSRTIACfgS(HSTIARTIA_5K);
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
AD5940_HPModeEn(bFALSE);
}
else if(freq<80000)
{
// hsdac_cfg.ExcitBufGain = AppIMPCfg.ExcitBufGain;
// hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
// hsdac_cfg.HsDacUpdateRate = 0x1B;
// AD5940_HSDacCfgS(&hsdac_cfg);
// AD5940_HSRTIACfgS(HSTIARTIA_5K);
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
AD5940_HPModeEn(bFALSE);
}
if(freq >= 80000)
{
// hsdac_cfg.ExcitBufGain = AppIMPCfg.ExcitBufGain;
// hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
// hsdac_cfg.HsDacUpdateRate = 0x07;
// AD5940_HSDacCfgS(&hsdac_cfg);
// AD5940_HSRTIACfgS(HSTIARTIA_5K);
filter_cfg.ADCRate = ADCRATE_1P6MHZ; filter_cfg.ADCRate = ADCRATE_1P6MHZ;
AppIMPCfg.AdcClkFreq = 32e6; AppIMPCfg.AdcClkFreq = 32e6;
AppIMPCfg.SysClkFreq = 32000000.0;
AD5940_HPModeEn(bTRUE); AD5940_HPModeEn(bTRUE);
} }
}
filter_cfg.ADCAvgNum = ADCAVGNUM_16; filter_cfg.ADCAvgNum = ADCAVGNUM_16;
filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr; filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr;
@ -463,8 +425,15 @@ AD5940Err AppIMPCheckFreq(float freq)
// Update Wait Times for all 3 measurements // Update Wait Times for all 3 measurements
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
SRAMAddr = AppIMPCfg.MeasureSeqInfo.SeqRamAddr + AppIMPCfg.SeqWaitAddr[i]; SRAMAddr = AppIMPCfg.MeasureSeqInfo.SeqRamAddr + AppIMPCfg.SeqWaitAddr[i];
SeqCmdBuff[0] = SEQ_WAIT(WaitClks/2);
SeqCmdBuff[1] = SEQ_WAIT(WaitClks/2); // CRITICAL FIX: Double the wait time for High Power Mode (>=80kHz)
uint32_t finalWait = WaitClks/2;
if (freq >= 80000) {
finalWait *= 2;
}
SeqCmdBuff[0] = SEQ_WAIT(finalWait);
SeqCmdBuff[1] = SEQ_WAIT(finalWait);
AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 2); AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 2);
} }
@ -551,6 +520,7 @@ int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData) if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData)
{ {
AD5940_WUPTCtrl(bFALSE); AD5940_WUPTCtrl(bFALSE);
AD5940_SEQCtrlS(bFALSE); // Added safety
return AD5940ERR_OK; return AD5940ERR_OK;
} }
} }
@ -662,7 +632,12 @@ int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
} }
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt); AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH); AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AppIMPRegModify(pBuff, &FifoCnt);
// Check if sweep is done
if (AppIMPRegModify(pBuff, &FifoCnt) != AD5940ERR_OK) {
// If sweep is done, we might want to signal main loop
}
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
AppIMPDataProcess((int32_t*)pBuff, &FifoCnt); AppIMPDataProcess((int32_t*)pBuff, &FifoCnt);
*pCount = FifoCnt; *pCount = FifoCnt;

View File

@ -1,3 +1,4 @@
// File: host/src/GraphWidget.cpp
#include "GraphWidget.h" #include "GraphWidget.h"
#include <limits> #include <limits>
#include <cmath> #include <cmath>
@ -13,27 +14,40 @@ GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent) {
// Setup Graphs // Setup Graphs
graph1 = plot->addGraph(); graph1 = plot->addGraph();
graph2 = plot->addGraph(plot->xAxis, plot->yAxis2); graph2 = plot->addGraph(plot->xAxis, plot->yAxis2);
graph3 = plot->addGraph(plot->xAxis, plot->yAxis2); // Hilbert Trace
// Style Graph 1 (Primary - Left Axis) // Style Graph 1 (Real - Blue)
QPen pen1(Qt::blue); QPen pen1(Qt::blue);
pen1.setWidth(2); pen1.setWidth(2);
graph1->setPen(pen1); graph1->setPen(pen1);
graph1->setLineStyle(QCPGraph::lsLine); graph1->setLineStyle(QCPGraph::lsLine);
graph1->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3)); graph1->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
graph1->setName("Primary"); graph1->setName("Real");
// Style Graph 2 (Secondary - Right Axis) // Style Graph 2 (Imaginary - Red)
QPen pen2(Qt::red); QPen pen2(Qt::red);
pen2.setWidth(2); pen2.setWidth(2);
graph2->setPen(pen2); graph2->setPen(pen2);
graph2->setLineStyle(QCPGraph::lsLine); graph2->setLineStyle(QCPGraph::lsLine);
graph2->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, 3)); graph2->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, 3));
graph2->setName("Secondary"); graph2->setName("Imaginary");
// Style Graph 3 (Hilbert - Green Dashed)
QPen pen3(Qt::green);
pen3.setWidth(2);
pen3.setStyle(Qt::DashLine);
graph3->setPen(pen3);
graph3->setLineStyle(QCPGraph::lsLine);
graph3->setName("Hilbert (Analytic)");
// Enable Right Axis // Enable Right Axis
plot->yAxis2->setVisible(true); plot->yAxis2->setVisible(true);
plot->yAxis2->setTickLabels(true); plot->yAxis2->setTickLabels(true);
// Link Axes for Zooming
connect(plot->yAxis, SIGNAL(rangeChanged(QCPRange)), plot->yAxis2, SLOT(setRange(QCPRange)));
connect(plot->yAxis2, SIGNAL(rangeChanged(QCPRange)), plot->yAxis, SLOT(setRange(QCPRange)));
// Interactions // Interactions
plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
@ -45,37 +59,7 @@ GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent) {
plot->legend->setBrush(QBrush(QColor(255, 255, 255, 230))); plot->legend->setBrush(QBrush(QColor(255, 255, 255, 230)));
// Default Mode // Default Mode
configureBodePlot(); configureRawPlot();
}
void GraphWidget::configureBodePlot() {
clear();
// X Axis: Frequency (Log)
plot->xAxis->setLabel("Frequency (Hz)");
plot->xAxis->setScaleType(QCPAxis::stLogarithmic);
QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog);
plot->xAxis->setTicker(logTicker);
plot->xAxis->setNumberFormat("eb");
// Y Axis 1: Impedance (Log)
plot->yAxis->setLabel("Impedance (Ohms)");
plot->yAxis->setScaleType(QCPAxis::stLogarithmic);
plot->yAxis->setTicker(logTicker);
plot->yAxis->setNumberFormat("eb");
// Y Axis 2: Phase (Linear)
plot->yAxis2->setLabel("Phase (Degrees)");
plot->yAxis2->setScaleType(QCPAxis::stLinear);
QSharedPointer<QCPAxisTicker> linTicker(new QCPAxisTicker);
plot->yAxis2->setTicker(linTicker);
plot->yAxis2->setNumberFormat("f");
plot->yAxis2->setRange(-180, 180);
graph1->setName("Impedance");
graph2->setName("Phase");
plot->replot();
} }
void GraphWidget::configureRawPlot() { void GraphWidget::configureRawPlot() {
@ -86,6 +70,7 @@ void GraphWidget::configureRawPlot() {
plot->xAxis->setScaleType(QCPAxis::stLogarithmic); plot->xAxis->setScaleType(QCPAxis::stLogarithmic);
QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog); QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog);
plot->xAxis->setTicker(logTicker); plot->xAxis->setTicker(logTicker);
plot->xAxis->setNumberFormat("eb");
// Y Axis 1: Real (Linear) // Y Axis 1: Real (Linear)
plot->yAxis->setLabel("Real (Ohms)"); plot->yAxis->setLabel("Real (Ohms)");
@ -99,50 +84,82 @@ void GraphWidget::configureRawPlot() {
plot->yAxis2->setScaleType(QCPAxis::stLinear); plot->yAxis2->setScaleType(QCPAxis::stLinear);
plot->yAxis2->setTicker(linTicker); plot->yAxis2->setTicker(linTicker);
plot->yAxis2->setNumberFormat("f"); plot->yAxis2->setNumberFormat("f");
plot->yAxis2->setVisible(true);
graph1->setName("Real"); graph1->setName("Real");
graph2->setName("Imaginary"); graph2->setName("Imaginary");
graph2->setVisible(true);
graph3->setVisible(true);
plot->replot(); plot->replot();
} }
void GraphWidget::addData(double freq, double val1, double val2) { void GraphWidget::configureNyquistPlot() {
// 1. Validate X-Axis (Frequency) clear();
// If Log scale, Frequency must be > 0.
if (plot->xAxis->scaleType() == QCPAxis::stLogarithmic && freq <= 0) { // X Axis: Real (Z')
return; plot->xAxis->setLabel("Real (Z')");
plot->xAxis->setScaleType(QCPAxis::stLinear);
QSharedPointer<QCPAxisTicker> linTicker(new QCPAxisTicker);
plot->xAxis->setTicker(linTicker);
plot->xAxis->setNumberFormat("f");
// Y Axis 1: -Imaginary (-Z'')
plot->yAxis->setLabel("-Imaginary (-Z'')");
plot->yAxis->setScaleType(QCPAxis::stLinear);
plot->yAxis->setTicker(linTicker);
plot->yAxis->setNumberFormat("f");
// Disable Secondary Axis for Nyquist
plot->yAxis2->setVisible(false);
graph2->setVisible(false);
graph3->setVisible(false);
graph1->setName("Nyquist");
graph1->setLineStyle(QCPGraph::lsLine);
graph1->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4));
plot->replot();
} }
// 2. Validate Y-Axis 1 (Impedance/Real) void GraphWidget::addData(double x, double val1, double val2) {
// If Log scale (Impedance), value must be > 0. // Mode Detection based on Axis Labels (Simple state check)
if (plot->yAxis->scaleType() == QCPAxis::stLogarithmic) { bool isNyquist = (plot->xAxis->label() == "Real (Z')");
// If we have a zero/negative impedance in log mode, skip it.
// Clamping to 1e-12 causes massive axis scaling issues (squished plots). if (isNyquist) {
if (val1 <= 1e-7) { // For Nyquist: x = Real, val1 = Imaginary
val1 = std::numeric_limits<double>::quiet_NaN(); // We plot Real vs -Imaginary
} graph1->addData(x, -val1);
} else {
// Raw Plot: x = Freq, val1 = Real, val2 = Imag
if (plot->xAxis->scaleType() == QCPAxis::stLogarithmic && x <= 0) return;
graph1->addData(x, val1);
graph2->addData(x, val2);
} }
// 3. Add Data // Auto-scale
// QCustomPlot handles NaN by creating a line gap, which is visually correct for missing/bad data.
if (!std::isnan(val1)) {
graph1->addData(freq, val1);
}
// Graph 2 (Phase/Imag) is usually Linear.
graph2->addData(freq, val2);
// 4. Auto-scale
// We remove 'true' (onlyEnlarge). We want the axes to shrink
// if we zoom in or if the data range stabilizes.
graph1->rescaleAxes(false); graph1->rescaleAxes(false);
if (!isNyquist) {
graph2->rescaleAxes(false); graph2->rescaleAxes(false);
graph3->rescaleAxes(false);
}
plot->replot(); plot->replot();
} }
void GraphWidget::addHilbertData(const QVector<double>& freq, const QVector<double>& hilbertImag) {
// Only valid for Raw Plot
if (plot->xAxis->label() != "Frequency (Hz)") return;
graph3->setData(freq, hilbertImag);
graph3->rescaleAxes(false);
plot->replot();
}
void GraphWidget::clear() { void GraphWidget::clear() {
graph1->data()->clear(); graph1->data()->clear();
graph2->data()->clear(); graph2->data()->clear();
graph3->data()->clear();
plot->replot(); plot->replot();
} }

View File

@ -1,3 +1,4 @@
// File: host/src/GraphWidget.h
#pragma once #pragma once
#include <QWidget> #include <QWidget>
@ -12,15 +13,17 @@ public:
// Data Handling // Data Handling
void addData(double freq, double val1, double val2); void addData(double freq, double val1, double val2);
void addHilbertData(const QVector<double>& freq, const QVector<double>& hilbertImag);
void clear(); void clear();
// View Configurations // View Configurations
void configureBodePlot();
void configureRawPlot(); void configureRawPlot();
void configureNyquistPlot();
private: private:
QVBoxLayout *layout; QVBoxLayout *layout;
QCustomPlot *plot; QCustomPlot *plot;
QCPGraph *graph1; QCPGraph *graph1; // Real
QCPGraph *graph2; QCPGraph *graph2; // Imaginary
QCPGraph *graph3; // Hilbert (Analytic Imaginary)
}; };

View File

@ -1,4 +1,4 @@
// host/src/MainWindow.cpp // File: host/src/MainWindow.cpp
#include "MainWindow.h" #include "MainWindow.h"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QDebug> #include <QDebug>
@ -10,6 +10,44 @@
#include <QLabel> #include <QLabel>
#include <QTimer> #include <QTimer>
#include <cmath> #include <cmath>
#include <complex>
#include <vector>
// Simple FFT Implementation (Cooley-Tukey)
// Note: Size must be power of 2
void fft(std::vector<std::complex<double>>& a) {
int n = a.size();
if (n <= 1) return;
std::vector<std::complex<double>> a0(n / 2), a1(n / 2);
for (int i = 0; i < n / 2; i++) {
a0[i] = a[2 * i];
a1[i] = a[2 * i + 1];
}
fft(a0);
fft(a1);
double ang = 2 * M_PI / n;
std::complex<double> w(1), wn(cos(ang), sin(ang));
for (int i = 0; i < n / 2; i++) {
a[i] = a0[i] + w * a1[i];
a[i + n / 2] = a0[i] - w * a1[i];
w *= wn;
}
}
void ifft(std::vector<std::complex<double>>& a) {
int n = a.size();
// Conjugate
for (int i = 0; i < n; i++) a[i] = std::conj(a[i]);
// Forward FFT
fft(a);
// Conjugate again and scale
for (int i = 0; i < n; i++) {
a[i] = std::conj(a[i]);
a[i] /= n;
}
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
serial = new QSerialPort(this); serial = new QSerialPort(this);
@ -37,13 +75,16 @@ void MainWindow::setupUi() {
// Tab Widget for Graphs // Tab Widget for Graphs
tabWidget = new QTabWidget(this); tabWidget = new QTabWidget(this);
finalGraph = new GraphWidget(this);
finalGraph->configureBodePlot();
rawGraph = new GraphWidget(this); rawGraph = new GraphWidget(this);
rawGraph->configureRawPlot(); rawGraph->configureRawPlot();
tabWidget->addTab(finalGraph, "Bode Plot"); nyquistGraph = new GraphWidget(this);
nyquistGraph->configureNyquistPlot();
// Raw Data is now Default (Index 0)
tabWidget->addTab(rawGraph, "Raw Data"); tabWidget->addTab(rawGraph, "Raw Data");
tabWidget->addTab(nyquistGraph, "Nyquist Plot");
// Log Widget // Log Widget
logWidget = new QTextEdit(this); logWidget = new QTextEdit(this);
@ -211,8 +252,12 @@ void MainWindow::runCalibration() {
void MainWindow::startSweep() { void MainWindow::startSweep() {
if (!serial->isOpen()) return; if (!serial->isOpen()) return;
finalGraph->clear();
rawGraph->clear(); rawGraph->clear();
nyquistGraph->clear();
// Clear accumulated data
sweepFreqs.clear();
sweepReals.clear();
// Use Firmware Sweep Command: s <start> <end> <steps> // Use Firmware Sweep Command: s <start> <end> <steps>
// Example: 100Hz to 200kHz, 50 steps // Example: 100Hz to 200kHz, 50 steps
@ -260,15 +305,69 @@ void MainWindow::parseData(const QString &data) {
QStringList parts = data.split(','); QStringList parts = data.split(',');
if (parts.size() < 6) return; if (parts.size() < 6) return;
bool okF, okM, okP, okR, okI; bool okF, okR, okI;
double freq = parts[1].toDouble(&okF); double freq = parts[1].toDouble(&okF);
double mag = parts[2].toDouble(&okM);
double phase= parts[3].toDouble(&okP);
double real = parts[4].toDouble(&okR); double real = parts[4].toDouble(&okR);
double imag = parts[5].toDouble(&okI); double imag = parts[5].toDouble(&okI);
if (okF && okM && okP) finalGraph->addData(freq, mag, phase); if (okF && okR && okI) {
if (okF && okR && okI) rawGraph->addData(freq, real, imag); // Add to Raw Graph (Freq vs Real/Imag)
rawGraph->addData(freq, real, imag);
// Add to Nyquist Graph (Real vs Imag)
nyquistGraph->addData(real, imag, 0);
// Accumulate for Hilbert
sweepFreqs.append(freq);
sweepReals.append(real);
// Check if sweep is done (e.g., 50 points)
// For now, update Hilbert every 10 points or at end
if (sweepReals.size() >= 50) {
computeHilbert();
}
}
}
void MainWindow::computeHilbert() {
int n = sweepReals.size();
if (n == 0) return;
// 1. Zero-pad to next power of 2
int n_fft = 1;
while (n_fft < n) n_fft *= 2;
std::vector<std::complex<double>> signal(n_fft);
for (int i = 0; i < n; i++) {
signal[i] = std::complex<double>(sweepReals[i], 0.0);
}
// 2. FFT
fft(signal);
// 3. Create Analytic Signal in Frequency Domain
// H[0] = H[0]
// H[i] = 2*H[i] for 0 < i < N/2
// H[N/2] = H[N/2]
// H[i] = 0 for N/2 < i < N
for (int i = 1; i < n_fft / 2; i++) {
signal[i] *= 2.0;
}
for (int i = n_fft / 2 + 1; i < n_fft; i++) {
signal[i] = 0.0;
}
// 4. IFFT
ifft(signal);
// 5. Extract Imaginary Part (Hilbert Transform)
QVector<double> hilbertImag;
for (int i = 0; i < n; i++) {
hilbertImag.append(signal[i].imag());
}
// 6. Plot
rawGraph->addHilbertData(sweepFreqs, hilbertImag);
} }
bool MainWindow::event(QEvent *event) { bool MainWindow::event(QEvent *event) {

View File

@ -1,4 +1,4 @@
// host/src/MainWindow.h // File: host/src/MainWindow.h
#pragma once #pragma once
#include <QMainWindow> #include <QMainWindow>
@ -42,12 +42,13 @@ private:
void setupUi(); void setupUi();
void parseData(const QString &data); void parseData(const QString &data);
void handleSwipe(QSwipeGesture *gesture); void handleSwipe(QSwipeGesture *gesture);
void computeHilbert();
QSerialPort *serial; QSerialPort *serial;
// Views // Views
GraphWidget *finalGraph; // Bode Plot GraphWidget *rawGraph; // Raw Data (Freq vs Real/Imag)
GraphWidget *rawGraph; // Raw Data GraphWidget *nyquistGraph; // Nyquist (Real vs -Imag)
QTextEdit *logWidget; // Serial Log QTextEdit *logWidget; // Serial Log
// Layout // Layout
@ -62,4 +63,8 @@ private:
QDoubleSpinBox *spinFreq; QDoubleSpinBox *spinFreq;
bool isMeasuring = false; bool isMeasuring = false;
// Data Accumulation for Hilbert
QVector<double> sweepFreqs;
QVector<double> sweepReals;
}; };

71
main.c
View File

@ -1,4 +1,4 @@
// main.c // File: main.c
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
@ -182,28 +182,20 @@ void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
char input_buffer[64]; char input_buffer[64];
int input_pos = 0; int input_pos = 0;
void process_command() { // Helper for robust state cleanup
char cmd = input_buffer[0]; void AD5940_Main_Cleanup(void) {
AppIMPCfg_Type *pCfg; // Ensure chip is awake before sending commands
AppIMPGetCfg(&pCfg); AD5940_WakeUp(10);
// CRITICAL: Stop any running measurement before reconfiguration // 1. Stop Sequencer and Wakeup Timer
AppIMPCtrl(IMPCTRL_STOPNOW, 0); AD5940_WUPTCtrl(bFALSE);
sleep_ms(10); // Give AFE time to halt AD5940_SEQCtrlS(bFALSE);
if (cmd == 'v') { // 2. Stop any active conversions/WG, but KEEP Reference/LDOs ON
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID); // This prevents settling issues on restart
printf("CHIP_ID:0x%04X\n", id); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bFALSE);
}
else if (cmd == 'c') {
// 1. CRITICAL FIX: Force System to 16MHz Low Power Mode
// This prevents the 32MHz post-sweep state from corrupting calibration timing
AD5940_HPModeEn(bFALSE);
// 2. Reset Analog Blocks to ensure clean state // 3. Reset FIFO
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
// 3. Disable and Reset the FIFO to stop "DATA" interrupts
FIFOCfg_Type fifo_cfg; FIFOCfg_Type fifo_cfg;
fifo_cfg.FIFOEn = bFALSE; fifo_cfg.FIFOEn = bFALSE;
AD5940_FIFOCfg(&fifo_cfg); AD5940_FIFOCfg(&fifo_cfg);
@ -215,8 +207,25 @@ void process_command() {
fifo_cfg.FIFOThresh = 6; fifo_cfg.FIFOThresh = 6;
AD5940_FIFOCfg(&fifo_cfg); AD5940_FIFOCfg(&fifo_cfg);
// 4. Clear any pending interrupts // 4. Clear Interrupts
AD5940_INTCClrFlag(AFEINTSRC_ALLINT); AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
}
void process_command() {
char cmd = input_buffer[0];
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
// CRITICAL: Perform robust cleanup before any new operation
AD5940_Main_Cleanup();
sleep_ms(10); // Give AFE time to settle
if (cmd == 'v') {
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID);
printf("CHIP_ID:0x%04X\n", id);
}
else if (cmd == 'c') {
// Cleanup already done by AD5940_Main_Cleanup() above
// 5. Perform ADC Calibration (Offset) // 5. Perform ADC Calibration (Offset)
ADCPGACal_Type adcpga_cal; ADCPGACal_Type adcpga_cal;
@ -282,7 +291,11 @@ void process_command() {
pCfg->SweepCfg.SweepEn = bFALSE; pCfg->SweepCfg.SweepEn = bFALSE;
pCfg->SinFreq = freq; pCfg->SinFreq = freq;
pCfg->NumOfData = -1; // Continuous pCfg->NumOfData = -1; // Continuous
pCfg->bParaChanged = bTRUE; // Force sequence regeneration
// CRITICAL FIX: Force parameter change flag to TRUE.
// This ensures AppIMPInit calls AppIMPSeqCfgGen, which re-enables
// the analog blocks (DAC, TIA, etc.) that AD5940_Main_Cleanup turned off.
pCfg->bParaChanged = bTRUE;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) { if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppIMPCtrl(IMPCTRL_START, 0); AppIMPCtrl(IMPCTRL_START, 0);
@ -302,7 +315,9 @@ void process_command() {
pCfg->SweepCfg.SweepPoints = steps; pCfg->SweepCfg.SweepPoints = steps;
pCfg->SweepCfg.SweepLog = bTRUE; pCfg->SweepCfg.SweepLog = bTRUE;
pCfg->NumOfData = steps; // Stop after sweep pCfg->NumOfData = steps; // Stop after sweep
pCfg->bParaChanged = bTRUE; // Force sequence regeneration
// CRITICAL FIX: Force parameter change flag to TRUE.
pCfg->bParaChanged = bTRUE;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) { if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppIMPCtrl(IMPCTRL_START, 0); AppIMPCtrl(IMPCTRL_START, 0);
@ -312,7 +327,7 @@ void process_command() {
} }
else if (cmd == 'x') { else if (cmd == 'x') {
// Stop Measurement // Stop Measurement
AppIMPCtrl(IMPCTRL_STOPNOW, 0); // Cleanup already done by AD5940_Main_Cleanup() at start of function
printf("STOPPED\n"); printf("STOPPED\n");
} }
else if (cmd == 'z') { else if (cmd == 'z') {
@ -348,6 +363,14 @@ int main() {
AppIMPISR(AppBuff, &temp); AppIMPISR(AppBuff, &temp);
if(temp > 0) { if(temp > 0) {
ImpedanceShowResult(AppBuff, temp); ImpedanceShowResult(AppBuff, temp);
// Check if sweep is done (simple check based on config)
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
if (pCfg->SweepCfg.SweepEn && pCfg->FifoDataCount >= pCfg->NumOfData) {
printf("DONE\n");
AD5940_Main_Cleanup(); // Ensure clean stop
}
} }
} }
} }