gettin gooder
This commit is contained in:
parent
98bd3534ea
commit
b0b0feb7b2
101
Impedance.c
101
Impedance.c
|
|
@ -1,4 +1,4 @@
|
|||
// Impedance.c
|
||||
// File: Impedance.c
|
||||
#include "ad5940.h"
|
||||
#include <stdio.h>
|
||||
#include "string.h"
|
||||
|
|
@ -295,7 +295,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
|
|||
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
|
||||
AFECTRL_SINC2NOTCH, 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_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[0]);
|
||||
|
|
@ -317,7 +318,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
|
|||
AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
|
||||
|
||||
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_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[1]);
|
||||
|
|
@ -335,7 +337,8 @@ static AD5940Err AppIMPSeqMeasureGen(void)
|
|||
AD5940_ADCMuxCfgS(ADCMUXP_AIN2, ADCMUXN_AIN3);
|
||||
|
||||
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_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[2]);
|
||||
|
|
@ -370,7 +373,6 @@ AD5940Err AppIMPCheckFreq(float freq)
|
|||
{
|
||||
ADCFilterCfg_Type filter_cfg;
|
||||
DFTCfg_Type dft_cfg;
|
||||
// HSDACCfg_Type hsdac_cfg; // Removed to prevent override
|
||||
uint32_t WaitClks;
|
||||
ClksCalInfo_Type clks_cal;
|
||||
FreqParams_Type freq_params;
|
||||
|
|
@ -379,64 +381,24 @@ AD5940Err AppIMPCheckFreq(float freq)
|
|||
|
||||
freq_params = AD5940_GetFreqParameters(freq);
|
||||
|
||||
// CRITICAL FIX: Removed HSDAC and HSRTIA overrides.
|
||||
// The configuration set in AppIMPSeqCfgGen (based on user config) should persist.
|
||||
// Overriding HSTIARTIA_5K here while RtiaVal remains 200 causes massive math errors.
|
||||
|
||||
if(freq < 0.51)
|
||||
// Only switch modes if necessary to avoid glitching the sequencer
|
||||
if(freq < 80000)
|
||||
{
|
||||
// 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);
|
||||
if (AppIMPCfg.SysClkFreq != 16000000.0) {
|
||||
filter_cfg.ADCRate = ADCRATE_800KHZ;
|
||||
AppIMPCfg.AdcClkFreq = 16e6;
|
||||
AppIMPCfg.SysClkFreq = 16000000.0;
|
||||
AD5940_HPModeEn(bFALSE);
|
||||
}
|
||||
}
|
||||
else if(freq < 5 )
|
||||
else
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
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;
|
||||
AppIMPCfg.AdcClkFreq = 32e6;
|
||||
AD5940_HPModeEn(bTRUE);
|
||||
if (AppIMPCfg.SysClkFreq != 32000000.0) {
|
||||
filter_cfg.ADCRate = ADCRATE_1P6MHZ;
|
||||
AppIMPCfg.AdcClkFreq = 32e6;
|
||||
AppIMPCfg.SysClkFreq = 32000000.0;
|
||||
AD5940_HPModeEn(bTRUE);
|
||||
}
|
||||
}
|
||||
|
||||
filter_cfg.ADCAvgNum = ADCAVGNUM_16;
|
||||
|
|
@ -463,8 +425,15 @@ AD5940Err AppIMPCheckFreq(float freq)
|
|||
// Update Wait Times for all 3 measurements
|
||||
for (int i = 0; i < 3; 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);
|
||||
}
|
||||
|
||||
|
|
@ -551,6 +520,7 @@ int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
|
|||
if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData)
|
||||
{
|
||||
AD5940_WUPTCtrl(bFALSE);
|
||||
AD5940_SEQCtrlS(bFALSE); // Added safety
|
||||
return AD5940ERR_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -662,7 +632,12 @@ int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
|
|||
}
|
||||
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
|
||||
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);
|
||||
AppIMPDataProcess((int32_t*)pBuff, &FifoCnt);
|
||||
*pCount = FifoCnt;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// File: host/src/GraphWidget.cpp
|
||||
#include "GraphWidget.h"
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
|
@ -13,26 +14,39 @@ GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent) {
|
|||
// Setup Graphs
|
||||
graph1 = plot->addGraph();
|
||||
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);
|
||||
pen1.setWidth(2);
|
||||
graph1->setPen(pen1);
|
||||
graph1->setLineStyle(QCPGraph::lsLine);
|
||||
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);
|
||||
pen2.setWidth(2);
|
||||
graph2->setPen(pen2);
|
||||
graph2->setLineStyle(QCPGraph::lsLine);
|
||||
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
|
||||
plot->yAxis2->setVisible(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
|
||||
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)));
|
||||
|
||||
// Default Mode
|
||||
configureBodePlot();
|
||||
}
|
||||
|
||||
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();
|
||||
configureRawPlot();
|
||||
}
|
||||
|
||||
void GraphWidget::configureRawPlot() {
|
||||
|
|
@ -86,6 +70,7 @@ void GraphWidget::configureRawPlot() {
|
|||
plot->xAxis->setScaleType(QCPAxis::stLogarithmic);
|
||||
QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog);
|
||||
plot->xAxis->setTicker(logTicker);
|
||||
plot->xAxis->setNumberFormat("eb");
|
||||
|
||||
// Y Axis 1: Real (Linear)
|
||||
plot->yAxis->setLabel("Real (Ohms)");
|
||||
|
|
@ -99,50 +84,82 @@ void GraphWidget::configureRawPlot() {
|
|||
plot->yAxis2->setScaleType(QCPAxis::stLinear);
|
||||
plot->yAxis2->setTicker(linTicker);
|
||||
plot->yAxis2->setNumberFormat("f");
|
||||
plot->yAxis2->setVisible(true);
|
||||
|
||||
graph1->setName("Real");
|
||||
graph2->setName("Imaginary");
|
||||
graph2->setVisible(true);
|
||||
graph3->setVisible(true);
|
||||
|
||||
plot->replot();
|
||||
}
|
||||
|
||||
void GraphWidget::addData(double freq, double val1, double val2) {
|
||||
// 1. Validate X-Axis (Frequency)
|
||||
// If Log scale, Frequency must be > 0.
|
||||
if (plot->xAxis->scaleType() == QCPAxis::stLogarithmic && freq <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Validate Y-Axis 1 (Impedance/Real)
|
||||
// If Log scale (Impedance), value must be > 0.
|
||||
if (plot->yAxis->scaleType() == QCPAxis::stLogarithmic) {
|
||||
// If we have a zero/negative impedance in log mode, skip it.
|
||||
// Clamping to 1e-12 causes massive axis scaling issues (squished plots).
|
||||
if (val1 <= 1e-7) {
|
||||
val1 = std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Add Data
|
||||
// QCustomPlot handles NaN by creating a line gap, which is visually correct for missing/bad data.
|
||||
if (!std::isnan(val1)) {
|
||||
graph1->addData(freq, val1);
|
||||
}
|
||||
void GraphWidget::configureNyquistPlot() {
|
||||
clear();
|
||||
|
||||
// Graph 2 (Phase/Imag) is usually Linear.
|
||||
graph2->addData(freq, val2);
|
||||
// X Axis: Real (Z')
|
||||
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");
|
||||
|
||||
// 4. Auto-scale
|
||||
// We remove 'true' (onlyEnlarge). We want the axes to shrink
|
||||
// if we zoom in or if the data range stabilizes.
|
||||
// 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();
|
||||
}
|
||||
|
||||
void GraphWidget::addData(double x, double val1, double val2) {
|
||||
// Mode Detection based on Axis Labels (Simple state check)
|
||||
bool isNyquist = (plot->xAxis->label() == "Real (Z')");
|
||||
|
||||
if (isNyquist) {
|
||||
// For Nyquist: x = Real, val1 = Imaginary
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Auto-scale
|
||||
graph1->rescaleAxes(false);
|
||||
graph2->rescaleAxes(false);
|
||||
if (!isNyquist) {
|
||||
graph2->rescaleAxes(false);
|
||||
graph3->rescaleAxes(false);
|
||||
}
|
||||
|
||||
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() {
|
||||
graph1->data()->clear();
|
||||
graph2->data()->clear();
|
||||
graph3->data()->clear();
|
||||
plot->replot();
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// File: host/src/GraphWidget.h
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
|
@ -12,15 +13,17 @@ public:
|
|||
|
||||
// Data Handling
|
||||
void addData(double freq, double val1, double val2);
|
||||
void addHilbertData(const QVector<double>& freq, const QVector<double>& hilbertImag);
|
||||
void clear();
|
||||
|
||||
// View Configurations
|
||||
void configureBodePlot();
|
||||
void configureRawPlot();
|
||||
void configureNyquistPlot();
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
QCustomPlot *plot;
|
||||
QCPGraph *graph1;
|
||||
QCPGraph *graph2;
|
||||
QCPGraph *graph1; // Real
|
||||
QCPGraph *graph2; // Imaginary
|
||||
QCPGraph *graph3; // Hilbert (Analytic Imaginary)
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// host/src/MainWindow.cpp
|
||||
// File: host/src/MainWindow.cpp
|
||||
#include "MainWindow.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QDebug>
|
||||
|
|
@ -10,6 +10,44 @@
|
|||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#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) {
|
||||
serial = new QSerialPort(this);
|
||||
|
|
@ -37,13 +75,16 @@ void MainWindow::setupUi() {
|
|||
|
||||
// Tab Widget for Graphs
|
||||
tabWidget = new QTabWidget(this);
|
||||
finalGraph = new GraphWidget(this);
|
||||
finalGraph->configureBodePlot();
|
||||
|
||||
rawGraph = new GraphWidget(this);
|
||||
rawGraph->configureRawPlot();
|
||||
|
||||
nyquistGraph = new GraphWidget(this);
|
||||
nyquistGraph->configureNyquistPlot();
|
||||
|
||||
tabWidget->addTab(finalGraph, "Bode Plot");
|
||||
// Raw Data is now Default (Index 0)
|
||||
tabWidget->addTab(rawGraph, "Raw Data");
|
||||
tabWidget->addTab(nyquistGraph, "Nyquist Plot");
|
||||
|
||||
// Log Widget
|
||||
logWidget = new QTextEdit(this);
|
||||
|
|
@ -211,8 +252,12 @@ void MainWindow::runCalibration() {
|
|||
void MainWindow::startSweep() {
|
||||
if (!serial->isOpen()) return;
|
||||
|
||||
finalGraph->clear();
|
||||
rawGraph->clear();
|
||||
nyquistGraph->clear();
|
||||
|
||||
// Clear accumulated data
|
||||
sweepFreqs.clear();
|
||||
sweepReals.clear();
|
||||
|
||||
// Use Firmware Sweep Command: s <start> <end> <steps>
|
||||
// Example: 100Hz to 200kHz, 50 steps
|
||||
|
|
@ -260,15 +305,69 @@ void MainWindow::parseData(const QString &data) {
|
|||
QStringList parts = data.split(',');
|
||||
if (parts.size() < 6) return;
|
||||
|
||||
bool okF, okM, okP, okR, okI;
|
||||
bool okF, okR, okI;
|
||||
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 imag = parts[5].toDouble(&okI);
|
||||
|
||||
if (okF && okM && okP) finalGraph->addData(freq, mag, phase);
|
||||
if (okF && okR && okI) rawGraph->addData(freq, real, imag);
|
||||
if (okF && okR && okI) {
|
||||
// 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) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// host/src/MainWindow.h
|
||||
// File: host/src/MainWindow.h
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
|
|
@ -42,13 +42,14 @@ private:
|
|||
void setupUi();
|
||||
void parseData(const QString &data);
|
||||
void handleSwipe(QSwipeGesture *gesture);
|
||||
void computeHilbert();
|
||||
|
||||
QSerialPort *serial;
|
||||
|
||||
// Views
|
||||
GraphWidget *finalGraph; // Bode Plot
|
||||
GraphWidget *rawGraph; // Raw Data
|
||||
QTextEdit *logWidget; // Serial Log
|
||||
GraphWidget *rawGraph; // Raw Data (Freq vs Real/Imag)
|
||||
GraphWidget *nyquistGraph; // Nyquist (Real vs -Imag)
|
||||
QTextEdit *logWidget; // Serial Log
|
||||
|
||||
// Layout
|
||||
QTabWidget *tabWidget;
|
||||
|
|
@ -62,4 +63,8 @@ private:
|
|||
QDoubleSpinBox *spinFreq;
|
||||
|
||||
bool isMeasuring = false;
|
||||
|
||||
// Data Accumulation for Hilbert
|
||||
QVector<double> sweepFreqs;
|
||||
QVector<double> sweepReals;
|
||||
};
|
||||
79
main.c
79
main.c
|
|
@ -1,4 +1,4 @@
|
|||
// main.c
|
||||
// File: main.c
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
|
@ -182,41 +182,50 @@ void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
|
|||
char input_buffer[64];
|
||||
int input_pos = 0;
|
||||
|
||||
// Helper for robust state cleanup
|
||||
void AD5940_Main_Cleanup(void) {
|
||||
// Ensure chip is awake before sending commands
|
||||
AD5940_WakeUp(10);
|
||||
|
||||
// 1. Stop Sequencer and Wakeup Timer
|
||||
AD5940_WUPTCtrl(bFALSE);
|
||||
AD5940_SEQCtrlS(bFALSE);
|
||||
|
||||
// 2. Stop any active conversions/WG, but KEEP Reference/LDOs ON
|
||||
// This prevents settling issues on restart
|
||||
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bFALSE);
|
||||
|
||||
// 3. Reset FIFO
|
||||
FIFOCfg_Type fifo_cfg;
|
||||
fifo_cfg.FIFOEn = bFALSE;
|
||||
AD5940_FIFOCfg(&fifo_cfg);
|
||||
|
||||
fifo_cfg.FIFOEn = bTRUE;
|
||||
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
||||
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
||||
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
||||
fifo_cfg.FIFOThresh = 6;
|
||||
AD5940_FIFOCfg(&fifo_cfg);
|
||||
|
||||
// 4. Clear Interrupts
|
||||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
||||
}
|
||||
|
||||
void process_command() {
|
||||
char cmd = input_buffer[0];
|
||||
AppIMPCfg_Type *pCfg;
|
||||
AppIMPGetCfg(&pCfg);
|
||||
|
||||
// CRITICAL: Stop any running measurement before reconfiguration
|
||||
AppIMPCtrl(IMPCTRL_STOPNOW, 0);
|
||||
sleep_ms(10); // Give AFE time to halt
|
||||
// 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') {
|
||||
// 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
|
||||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||
|
||||
// 3. Disable and Reset the FIFO to stop "DATA" interrupts
|
||||
FIFOCfg_Type fifo_cfg;
|
||||
fifo_cfg.FIFOEn = bFALSE;
|
||||
AD5940_FIFOCfg(&fifo_cfg);
|
||||
|
||||
fifo_cfg.FIFOEn = bTRUE;
|
||||
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
||||
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
||||
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
||||
fifo_cfg.FIFOThresh = 6;
|
||||
AD5940_FIFOCfg(&fifo_cfg);
|
||||
|
||||
// 4. Clear any pending interrupts
|
||||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
||||
// Cleanup already done by AD5940_Main_Cleanup() above
|
||||
|
||||
// 5. Perform ADC Calibration (Offset)
|
||||
ADCPGACal_Type adcpga_cal;
|
||||
|
|
@ -282,7 +291,11 @@ void process_command() {
|
|||
pCfg->SweepCfg.SweepEn = bFALSE;
|
||||
pCfg->SinFreq = freq;
|
||||
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) {
|
||||
AppIMPCtrl(IMPCTRL_START, 0);
|
||||
|
|
@ -302,7 +315,9 @@ void process_command() {
|
|||
pCfg->SweepCfg.SweepPoints = steps;
|
||||
pCfg->SweepCfg.SweepLog = bTRUE;
|
||||
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) {
|
||||
AppIMPCtrl(IMPCTRL_START, 0);
|
||||
|
|
@ -312,7 +327,7 @@ void process_command() {
|
|||
}
|
||||
else if (cmd == 'x') {
|
||||
// Stop Measurement
|
||||
AppIMPCtrl(IMPCTRL_STOPNOW, 0);
|
||||
// Cleanup already done by AD5940_Main_Cleanup() at start of function
|
||||
printf("STOPPED\n");
|
||||
}
|
||||
else if (cmd == 'z') {
|
||||
|
|
@ -348,6 +363,14 @@ int main() {
|
|||
AppIMPISR(AppBuff, &temp);
|
||||
if(temp > 0) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue