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 "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,64 +381,24 @@ 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;
|
filter_cfg.ADCRate = ADCRATE_800KHZ;
|
||||||
// hsdac_cfg.HsDacUpdateRate = 0x1B;
|
AppIMPCfg.AdcClkFreq = 16e6;
|
||||||
// AD5940_HSDacCfgS(&hsdac_cfg);
|
AppIMPCfg.SysClkFreq = 16000000.0;
|
||||||
// AD5940_HSRTIACfgS(HSTIARTIA_40K);
|
AD5940_HPModeEn(bFALSE);
|
||||||
filter_cfg.ADCRate = ADCRATE_800KHZ;
|
}
|
||||||
AppIMPCfg.AdcClkFreq = 16e6;
|
|
||||||
AD5940_HPModeEn(bFALSE);
|
|
||||||
}
|
}
|
||||||
else if(freq < 5 )
|
else
|
||||||
{
|
{
|
||||||
// hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2;
|
if (AppIMPCfg.SysClkFreq != 32000000.0) {
|
||||||
// hsdac_cfg.HsDacGain = HSDACGAIN_1;
|
filter_cfg.ADCRate = ADCRATE_1P6MHZ;
|
||||||
// hsdac_cfg.HsDacUpdateRate = 0x1B;
|
AppIMPCfg.AdcClkFreq = 32e6;
|
||||||
// AD5940_HSDacCfgS(&hsdac_cfg);
|
AppIMPCfg.SysClkFreq = 32000000.0;
|
||||||
// AD5940_HSRTIACfgS(HSTIARTIA_40K);
|
AD5940_HPModeEn(bTRUE);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_cfg.ADCAvgNum = ADCAVGNUM_16;
|
filter_cfg.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// File: host/src/GraphWidget.cpp
|
||||||
#include "GraphWidget.h"
|
#include "GraphWidget.h"
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
@ -13,26 +14,39 @@ 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) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Graph 2 (Phase/Imag) is usually Linear.
|
// X Axis: Real (Z')
|
||||||
graph2->addData(freq, val2);
|
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
|
// Disable Secondary Axis for Nyquist
|
||||||
// We remove 'true' (onlyEnlarge). We want the axes to shrink
|
plot->yAxis2->setVisible(false);
|
||||||
// if we zoom in or if the data range stabilizes.
|
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);
|
graph1->rescaleAxes(false);
|
||||||
graph2->rescaleAxes(false);
|
if (!isNyquist) {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
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(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) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// host/src/MainWindow.h
|
// File: host/src/MainWindow.h
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
@ -42,13 +42,14 @@ 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
|
||||||
QTabWidget *tabWidget;
|
QTabWidget *tabWidget;
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
79
main.c
79
main.c
|
|
@ -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,41 +182,50 @@ void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
|
||||||
char input_buffer[64];
|
char input_buffer[64];
|
||||||
int input_pos = 0;
|
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() {
|
void process_command() {
|
||||||
char cmd = input_buffer[0];
|
char cmd = input_buffer[0];
|
||||||
AppIMPCfg_Type *pCfg;
|
AppIMPCfg_Type *pCfg;
|
||||||
AppIMPGetCfg(&pCfg);
|
AppIMPGetCfg(&pCfg);
|
||||||
|
|
||||||
// CRITICAL: Stop any running measurement before reconfiguration
|
// CRITICAL: Perform robust cleanup before any new operation
|
||||||
AppIMPCtrl(IMPCTRL_STOPNOW, 0);
|
AD5940_Main_Cleanup();
|
||||||
sleep_ms(10); // Give AFE time to halt
|
sleep_ms(10); // Give AFE time to settle
|
||||||
|
|
||||||
if (cmd == 'v') {
|
if (cmd == 'v') {
|
||||||
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID);
|
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID);
|
||||||
printf("CHIP_ID:0x%04X\n", id);
|
printf("CHIP_ID:0x%04X\n", id);
|
||||||
}
|
}
|
||||||
else if (cmd == 'c') {
|
else if (cmd == 'c') {
|
||||||
// 1. CRITICAL FIX: Force System to 16MHz Low Power Mode
|
// Cleanup already done by AD5940_Main_Cleanup() above
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue