This commit is contained in:
pszsh 2026-01-31 21:37:27 -08:00
parent 4266c4b672
commit 6adf55bc47
13 changed files with 2691 additions and 324 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@ build*
*.png
icons/
*.pdf
*.txt
examples/
build/

495
Amperometric.c Normal file
View File

@ -0,0 +1,495 @@
// Amperometric.c
/*!
*****************************************************************************
@file: Amperometric.c
@author: $Author: mlambe $
@brief: Amperometric measurement.
@version: $Revision: 766 $
@date: $Date: 2018-03-21 14:09:35 +0100 (Wed, 21 Mar 2018) $
-----------------------------------------------------------------------------
Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved.
This software is proprietary to Analog Devices, Inc. and its licensors.
By using this software you agree to the terms of the associated
Analog Devices Software License Agreement.
*****************************************************************************/
#include "Amperometric.h"
#define AD5940ERR_STOP 10
/*
Application configuration structure. Specified by user from template.
The variables are usable in this whole application.
It includes basic configuration for sequencer generator and application related parameters
*/
AppAMPCfg_Type AppAMPCfg =
{
.bParaChanged = bFALSE,
.SeqStartAddr = 0,
.MaxSeqLen = 0,
.SeqStartAddrCal = 0,
.MaxSeqLenCal = 0,
.FifoThresh = 5, /* Number of points for FIFO */
.SysClkFreq = 16000000.0,
.WuptClkFreq = 32000.0,
.AdcClkFreq = 16000000.0,
.AmpODR = 1.0, /* Sample time in seconds. I.e. every 5 seconds make a measurement */
.NumOfData = -1,
.RcalVal = 100.0, /* RCAL = 100 Ohms */
.PwrMod = AFEPWR_LP,
.AMPInited = bFALSE,
.StopRequired = bFALSE,
/* LPTIA Configure */
.ExtRtia = bFALSE, /* Set to true if using external RTIA */
.LptiaRtiaSel = LPTIARTIA_4K, /* COnfigure RTIA */
.LpTiaRf = LPTIARF_1M, /* Configure LPF resistor */
.LpTiaRl = LPTIARLOAD_100R,
.ReDoRtiaCal = bTRUE,
.RtiaCalValue = 0,
.ExtRtiaVal = 0,
/*LPDAC Configure */
.Vzero = 1100, /* Sets voltage on SE0 and LPTIA */
.SensorBias = 500, /* Sets voltage between RE0 and SE0 */
/* ADC Configure*/
.ADCPgaGain = ADCPGA_1P5,
.ADCSinc3Osr = ADCSINC3OSR_4,
.ADCSinc2Osr = ADCSINC2OSR_22,
.DataFifoSrc = FIFOSRC_SINC2NOTCH,
.ADCRefVolt = 1.8162, /* Measure voltage on ADCRefVolt pin and enter here*/
};
/**
This function is provided for upper controllers that want to change
application parameters specially for user defined parameters.
*/
AD5940Err AppAMPGetCfg(void *pCfg)
{
if(pCfg){
*(AppAMPCfg_Type**)pCfg = &AppAMPCfg;
return AD5940ERR_OK;
}
return AD5940ERR_PARA;
}
AD5940Err AppAMPCtrl(int32_t AmpCtrl, void *pPara)
{
switch (AmpCtrl)
{
case AMPCTRL_START:
{
WUPTCfg_Type wupt_cfg;
AD5940_ReadReg(REG_AFE_ADCDAT); /* Any SPI Operation can wakeup AFE */
if(AppAMPCfg.AMPInited == bFALSE)
return AD5940ERR_APPERROR;
/* Start it */
wupt_cfg.WuptEn = bTRUE;
wupt_cfg.WuptEndSeq = WUPTENDSEQ_A;
wupt_cfg.WuptOrder[0] = SEQID_0;
wupt_cfg.SeqxSleepTime[SEQID_0] = 4-1;
wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppAMPCfg.WuptClkFreq*AppAMPCfg.AmpODR)-4-1;
AD5940_WUPTCfg(&wupt_cfg);
AppAMPCfg.FifoDataCount = 0; /* restart */
break;
}
case AMPCTRL_STOPNOW:
{
AD5940_ReadReg(REG_AFE_ADCDAT); /* Any SPI Operation can wakeup AFE */
/* Start Wupt right now */
AD5940_WUPTCtrl(bFALSE);
/* There is chance this operation will fail because sequencer could put AFE back
to hibernate mode just after waking up. Use STOPSYNC is better. */
AD5940_WUPTCtrl(bFALSE);
break;
}
case AMPCTRL_STOPSYNC:
{
AppAMPCfg.StopRequired = bTRUE;
break;
}
case AMPCTRL_SHUTDOWN:
{
AppAMPCtrl(AMPCTRL_STOPNOW, 0); /* Stop the measurement if it's running. */
/* Turn off LPloop related blocks which are not controlled automatically by sleep operation */
AFERefCfg_Type aferef_cfg;
LPLoopCfg_Type lp_loop;
memset(&aferef_cfg, 0, sizeof(aferef_cfg));
AD5940_REFCfgS(&aferef_cfg);
memset(&lp_loop, 0, sizeof(lp_loop));
AD5940_LPLoopCfgS(&lp_loop);
AD5940_EnterSleepS(); /* Enter Hibernate */
}
break;
default:
break;
}
return AD5940ERR_OK;
}
/* Generate init sequence */
static AD5940Err AppAMPSeqCfgGen(void)
{
AD5940Err error = AD5940ERR_OK;
uint32_t const *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
LPLoopCfg_Type lp_loop;
DSPCfg_Type dsp_cfg;
SWMatrixCfg_Type sw_cfg;
/* Start sequence generator here */
AD5940_SEQGenCtrl(bTRUE);
//AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Init all to disable state */
aferef_cfg.HpBandgapEn = bTRUE;
aferef_cfg.Hp1V1BuffEn = bTRUE;
aferef_cfg.Hp1V8BuffEn = bTRUE;
aferef_cfg.Disc1V1Cap = bFALSE;
aferef_cfg.Disc1V8Cap = bFALSE;
aferef_cfg.Hp1V8ThemBuff = bFALSE;
aferef_cfg.Hp1V8Ilimit = bFALSE;
aferef_cfg.Lp1V1BuffEn = bTRUE;
aferef_cfg.Lp1V8BuffEn = bTRUE;
/* LP reference control - turn off them to save power*/
aferef_cfg.LpBandgapEn = bTRUE;
aferef_cfg.LpRefBufEn = bTRUE;
aferef_cfg.LpRefBoostEn = bFALSE;
AD5940_REFCfgS(&aferef_cfg);
lp_loop.LpDacCfg.LpdacSel = LPDAC0;
lp_loop.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
lp_loop.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VBIAS2PIN|LPDACSW_VZERO2LPTIA|LPDACSW_VZERO2PIN;
lp_loop.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
lp_loop.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
lp_loop.LpDacCfg.LpDacRef = LPDACREF_2P5;
lp_loop.LpDacCfg.DataRst = bFALSE;
lp_loop.LpDacCfg.PowerEn = bTRUE;
lp_loop.LpDacCfg.DacData6Bit = (uint32_t)((AppAMPCfg.Vzero-200)/DAC6BITVOLT_1LSB);
lp_loop.LpDacCfg.DacData12Bit =(int32_t)((AppAMPCfg.SensorBias)/DAC12BITVOLT_1LSB) + lp_loop.LpDacCfg.DacData6Bit*64;
if(lp_loop.LpDacCfg.DacData12Bit>lp_loop.LpDacCfg.DacData6Bit*64)
lp_loop.LpDacCfg.DacData12Bit--;
lp_loop.LpAmpCfg.LpAmpSel = LPAMP0;
lp_loop.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
lp_loop.LpAmpCfg.LpPaPwrEn = bTRUE;
lp_loop.LpAmpCfg.LpTiaPwrEn = bTRUE;
lp_loop.LpAmpCfg.LpTiaRf = AppAMPCfg.LpTiaRf;
lp_loop.LpAmpCfg.LpTiaRload = AppAMPCfg.LpTiaRl;
if(AppAMPCfg.ExtRtia == bTRUE)
{
lp_loop.LpAmpCfg.LpTiaRtia = LPTIARTIA_OPEN;
lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(9)|LPTIASW(2)|LPTIASW(4)|LPTIASW(5)|LPTIASW(12)|LPTIASW(13);
}else
{
lp_loop.LpAmpCfg.LpTiaRtia = AppAMPCfg.LptiaRtiaSel;
lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
}
AD5940_LPLoopCfgS(&lp_loop);
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_VZERO0;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_AIN4;
dsp_cfg.ADCBaseCfg.ADCPga = AppAMPCfg.ADCPgaGain;
memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
memset(&dsp_cfg.DftCfg, 0, sizeof(dsp_cfg.DftCfg));
dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16; /* Don't care because it's disabled */
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ; /* Tell filter block clock rate of ADC*/
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppAMPCfg.ADCSinc2Osr;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppAMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
dsp_cfg.ADCFilterCfg.BpNotch = bFALSE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg)); /* Don't care about Statistic */
AD5940_DSPCfgS(&dsp_cfg);
sw_cfg.Dswitch = 0;
sw_cfg.Pswitch = 0;
sw_cfg.Nswitch = 0;
sw_cfg.Tswitch = 0;
AD5940_SWMatrixCfgS(&sw_cfg);
/* Enable all of them. They are automatically turned off during hibernate mode to save power */
AD5940_AFECtrlS(AFECTRL_HPREFPWR|AFECTRL_SINC2NOTCH, bTRUE);
AD5940_AFECtrlS(AFECTRL_SINC2NOTCH, bFALSE);
AD5940_SEQGpioCtrlS(0/*AGPIO_Pin6|AGPIO_Pin5|AGPIO_Pin1*/); //GP6->endSeq, GP5 -> AD8233=OFF, GP1->RLD=OFF .
/* Sequence end. */
AD5940_SEQGenInsert(SEQ_STOP()); /* Add one extra command to disable sequencer for initialization sequence because we only want it to run one time. */
/* Stop here */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
if(error == AD5940ERR_OK)
{
AppAMPCfg.InitSeqInfo.SeqId = SEQID_1;
AppAMPCfg.InitSeqInfo.SeqRamAddr = AppAMPCfg.SeqStartAddr;
AppAMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppAMPCfg.InitSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppAMPCfg.InitSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
static AD5940Err AppAMPSeqMeasureGen(void)
{
AD5940Err error = AD5940ERR_OK;
uint32_t const *pSeqCmd;
uint32_t SeqLen;
uint32_t WaitClks;
ClksCalInfo_Type clks_cal;
clks_cal.DataType = DATATYPE_SINC2;
clks_cal.DataCount = 1;
clks_cal.ADCSinc2Osr = AppAMPCfg.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = AppAMPCfg.ADCSinc3Osr;
clks_cal.ADCAvgNum = 0;
clks_cal.RatioSys2AdcClk = AppAMPCfg.SysClkFreq/AppAMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
WaitClks += 15;
AD5940_SEQGenCtrl(bTRUE);
AD5940_SEQGpioCtrlS(AGPIO_Pin2);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_SINC2NOTCH, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*250)); /* wait 250us */
AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE); /* Start ADC convert*/
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_ADCCNV|AFECTRL_SINC2NOTCH, bFALSE); /* Stop ADC */
AD5940_SEQGpioCtrlS(0);
AD5940_EnterSleepS();/* Goto hibernate */
/* Sequence end. */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
if(error == AD5940ERR_OK)
{
AppAMPCfg.MeasureSeqInfo.SeqId = SEQID_0;
AppAMPCfg.MeasureSeqInfo.SeqRamAddr = AppAMPCfg.InitSeqInfo.SeqRamAddr + AppAMPCfg.InitSeqInfo.SeqLen ;
AppAMPCfg.MeasureSeqInfo.pSeqCmd = pSeqCmd;
AppAMPCfg.MeasureSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppAMPCfg.MeasureSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
static AD5940Err AppAMPRtiaCal(void)
{
fImpPol_Type RtiaCalValue; /* Calibration result */
LPRTIACal_Type lprtia_cal;
AD5940_StructInit(&lprtia_cal, sizeof(lprtia_cal));
lprtia_cal.bPolarResult = bTRUE; /* Magnitude + Phase */
lprtia_cal.AdcClkFreq = AppAMPCfg.AdcClkFreq;
lprtia_cal.SysClkFreq = AppAMPCfg.SysClkFreq;
lprtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
lprtia_cal.ADCSinc2Osr = ADCSINC2OSR_22; /* Use SINC2 data as DFT data source */
lprtia_cal.DftCfg.DftNum = DFTNUM_2048; /* Maximum DFT number */
lprtia_cal.DftCfg.DftSrc = DFTSRC_SINC2NOTCH; /* For frequency under 12Hz, need to optimize DFT source. Use SINC3 data as DFT source */
lprtia_cal.DftCfg.HanWinEn = bTRUE;
lprtia_cal.fFreq = AppAMPCfg.AdcClkFreq/4/22/2048*3; /* Sample 3 period of signal, 13.317Hz here. Do not use DC method, because it needs ADC/PGA calibrated firstly(but it's faster) */
lprtia_cal.fRcal = AppAMPCfg.RcalVal;
lprtia_cal.LpTiaRtia = AppAMPCfg.LptiaRtiaSel;
lprtia_cal.LpAmpPwrMod = LPAMPPWR_NORM;
lprtia_cal.bWithCtia = bFALSE;
AD5940_LPRtiaCal(&lprtia_cal, &RtiaCalValue);
AppAMPCfg.RtiaCalValue = RtiaCalValue;
return AD5940ERR_OK;
}
/* This function provide application initialize. */
AD5940Err AppAMPInit(uint32_t *pBuffer, uint32_t BufferSize)
{
AD5940Err error = AD5940ERR_OK;
SEQCfg_Type seq_cfg;
FIFOCfg_Type fifo_cfg;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
/* Configure sequencer and stop it */
seq_cfg.SeqMemSize = SEQMEMSIZE_2KB; /* 2kB SRAM is used for sequencer, others for data FIFO */
seq_cfg.SeqBreakEn = bFALSE;
seq_cfg.SeqIgnoreEn = bFALSE;
seq_cfg.SeqCntCRCClr = bTRUE;
seq_cfg.SeqEnable = bFALSE;
seq_cfg.SeqWrTimer = 0;
AD5940_SEQCfg(&seq_cfg);
/* Do RTIA calibration */
if(((AppAMPCfg.ReDoRtiaCal == bTRUE) || \
AppAMPCfg.AMPInited == bFALSE) && AppAMPCfg.ExtRtia == bFALSE) /* Do calibration on the first initializaion */
{
AppAMPRtiaCal();
AppAMPCfg.ReDoRtiaCal = bFALSE;
}else
AppAMPCfg.RtiaCalValue.Magnitude = AppAMPCfg.ExtRtiaVal;
/* Reconfigure FIFO */
AD5940_FIFOCtrlS(DFTSRC_SINC3, bFALSE); /* Disable FIFO firstly */
fifo_cfg.FIFOEn = bTRUE;
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
fifo_cfg.FIFOSize = FIFOSIZE_4KB; /* 4kB for FIFO, The reset 2kB for sequencer */
fifo_cfg.FIFOSrc = AppAMPCfg.DataFifoSrc;
fifo_cfg.FIFOThresh = AppAMPCfg.FifoThresh;
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
/* Start sequence generator */
/* Initialize sequencer generator */
if((AppAMPCfg.AMPInited == bFALSE)||\
(AppAMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
AD5940_SEQGenInit(pBuffer, BufferSize);
/* Generate initialize sequence */
error = AppAMPSeqCfgGen(); /* Application initialization sequence using either MCU or sequencer */
if(error != AD5940ERR_OK) return error;
/* Generate measurement sequence */
error = AppAMPSeqMeasureGen();
if(error != AD5940ERR_OK) return error;
AppAMPCfg.bParaChanged = bFALSE; /* Clear this flag as we already implemented the new configuration */
}
/* Initialization sequencer */
AppAMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppAMPCfg.InitSeqInfo);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg); /* Enable sequencer */
AD5940_SEQMmrTrig(AppAMPCfg.InitSeqInfo.SeqId);
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
/* Measurement sequence */
AppAMPCfg.MeasureSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppAMPCfg.MeasureSeqInfo);
// seq_cfg.SeqEnable = bTRUE;
// AD5940_SEQCfg(&seq_cfg); /* Enable sequencer, and wait for trigger */
AD5940_SEQCtrlS(bTRUE); /* Enable sequencer, and wait for trigger. It's disabled in initialization sequence */
AD5940_ClrMCUIntFlag(); /* Clear interrupt flag generated before */
AD5940_AFEPwrBW(AppAMPCfg.PwrMod, AFEBW_250KHZ);
AppAMPCfg.AMPInited = bTRUE; /* AMP application has been initialized. */
return AD5940ERR_OK;
}
/* Modify registers when AFE wakeup */
static AD5940Err AppAMPRegModify(int32_t * const pData, uint32_t *pDataCount)
{
if(AppAMPCfg.NumOfData > 0)
{
AppAMPCfg.FifoDataCount += *pDataCount/4;
if(AppAMPCfg.FifoDataCount >= AppAMPCfg.NumOfData)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_STOP; // Return STOP code
}
}
if(AppAMPCfg.StopRequired == bTRUE)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
return AD5940ERR_OK;
}
/* Depending on the data type, do appropriate data pre-process before return back to controller */
static AD5940Err AppAMPDataProcess(int32_t * const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
float *pOut = (float *)pData;
for(i=0;i<datacount;i++)
{
pData[i] &= 0xffff;
pOut[i] = AppAMPCalcCurrent(pData[i]);
}
return AD5940ERR_OK;
}
/**
*/
AD5940Err AppAMPISR(void *pBuff, uint32_t *pCount)
{
uint32_t FifoCnt;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
AD5940_SleepKeyCtrlS(SLPKEY_LOCK);
*pCount = 0;
if(AD5940_INTCTestFlag(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH) == bTRUE)
{
FifoCnt = AD5940_FIFOGetCnt();
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AD5940Err status = AppAMPRegModify(pBuff, &FifoCnt); /* If there is need to do AFE re-configure, do it here when AFE is in active state */
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
//AD5940_EnterSleepS(); /* Manually put AFE back to hibernate mode. This operation only takes effect when register value is ACTIVE previously */
/* Process data */
AppAMPDataProcess((int32_t*)pBuff,&FifoCnt);
*pCount = FifoCnt;
if (status == AD5940ERR_STOP) return AD5940ERR_STOP;
return 0;
}
return 0;
}
/* Calculate voltage */
float AppAMPCalcVoltage(uint32_t ADCcode)
{
float kFactor = 1.835/1.82;
float fVolt = 0.0;
int32_t tmp = 0;
tmp = ADCcode - 32768;
switch(AppAMPCfg.ADCPgaGain)
{
case ADCPGA_1:
fVolt = ((float)(tmp)/32768)*(AppAMPCfg.ADCRefVolt/1)*kFactor;
break;
case ADCPGA_1P5:
fVolt = ((float)(tmp)/32768)*(AppAMPCfg.ADCRefVolt/1.5f)*kFactor;
break;
case ADCPGA_2:
fVolt = ((float)(tmp)/32768)*(AppAMPCfg.ADCRefVolt/2)*kFactor;
break;
case ADCPGA_4:
fVolt = ((float)(tmp)/32768)*(AppAMPCfg.ADCRefVolt/4)*kFactor;
break;
case ADCPGA_9:
fVolt = ((float)(tmp)/32768)*(AppAMPCfg.ADCRefVolt/9)*kFactor;
break;
}
return fVolt;
}
/* Calculate current in uA */
float AppAMPCalcCurrent(uint32_t ADCcode)
{
float fCurrent, fVoltage = 0.0;
fVoltage = AppAMPCalcVoltage(ADCcode);
fCurrent = fVoltage/AppAMPCfg.RtiaCalValue.Magnitude;
return -fCurrent*1000000;
}

95
Amperometric.h Normal file
View File

@ -0,0 +1,95 @@
// File: Amperometric.h
/*!
*****************************************************************************
@file: Amperometric.h
@author: $Author: mlambe $
@brief: Amperometric measurement header file.
@version: $Revision: 766 $
@date: $Date: 2018-03-21 14:09:35 +0100 (Wed, 21 Mar 2018) $
-----------------------------------------------------------------------------
Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved.
This software is proprietary to Analog Devices, Inc. and its licensors.
By using this software you agree to the terms of the associated
Analog Devices Software License Agreement.
*****************************************************************************/
#ifndef _AMPEROMETRIC_H_
#define _AMPEROMETRIC_H_
#include "ad5940.h"
#include "stdio.h"
#include "string.h"
#include "math.h"
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
/*
Note: this example will use SEQID_0 as measurement sequence, and use SEQID_1 as init sequence.
SEQID_3 is used for calibration.
*/
typedef struct
{
/* Common configurations for all kinds of Application. */
BoolFlag bParaChanged; /* Indicate to generate sequence again. It's auto cleared by AppAMPInit */
uint32_t SeqStartAddr; /* Initialaztion sequence start address in SRAM of AD5940 */
uint32_t MaxSeqLen; /* Limit the maximum sequence. */
uint32_t SeqStartAddrCal; /* Measurement sequence start address in SRAM of AD5940 */
uint32_t MaxSeqLenCal;
/* Application related parameters */
BoolFlag ReDoRtiaCal; /* Set this flag to bTRUE when there is need to do calibration. */
float SysClkFreq; /* The real frequency of system clock */
float WuptClkFreq; /* The clock frequency of Wakeup Timer in Hz. Typically it's 32kHz. Leave it here in case we calibrate clock in software method */
float AdcClkFreq; /* The real frequency of ADC clock */
uint32_t FifoThresh; /* FIFO threshold. Should be N*4 */
float AmpODR; /* in Hz. ODR decides the period of WakeupTimer who will trigger sequencer periodically.*/
int32_t NumOfData; /* By default it's '-1'. If you want the engine stops after get NumofData, then set the value here. Otherwise, set it to '-1' which means never stop. */
float RcalVal; /* Rcal value in Ohm */
float ADCRefVolt; /* Measured 1.82 V reference*/
uint32_t PwrMod; /* Control Chip power mode(LP/HP) */
uint32_t ADCPgaGain; /* PGA Gain select from GNPGA_1, GNPGA_1_5, GNPGA_2, GNPGA_4, GNPGA_9 !!! We must ensure signal is in range of +-1.5V which is limited by ADC input stage */
uint8_t ADCSinc3Osr; /* SINC3 OSR selection. ADCSINC3OSR_2, ADCSINC3OSR_4 */
uint8_t ADCSinc2Osr; /* SINC2 OSR selection. ADCSINC2OSR_22...ADCSINC2OSR_1333 */
uint32_t DataFifoSrc; /* DataFIFO source. FIFOSRC_SINC3, FIFOSRC_DFT, FIFOSRC_SINC2NOTCH, FIFOSRC_VAR, FIFOSRC_MEAN*/
uint32_t LptiaRtiaSel; /* Use internal RTIA, select from RTIA_INT_200, RTIA_INT_1K, RTIA_INT_5K, RTIA_INT_10K, RTIA_INT_20K, RTIA_INT_40K, RTIA_INT_80K, RTIA_INT_160K */
uint32_t LpTiaRf; /* Rfilter select */
uint32_t LpTiaRl; /* SE0 Rload select */
fImpPol_Type RtiaCalValue; /* Calibrated Rtia value */
float Vzero; /* Voltage on SE0 pin and Vzero, optimumly 1100mV*/
float SensorBias; /* Sensor bias voltage = VRE0 - VSE0 */
BoolFlag ExtRtia; /* Use internal or external Rtia */
float ExtRtiaVal; /* External Rtia value if using one */
BoolFlag AMPInited; /* If the program run firstly, generated sequence commands */
SEQInfo_Type InitSeqInfo;
SEQInfo_Type MeasureSeqInfo;
BoolFlag StopRequired; /* After FIFO is ready, stop the measurement sequence */
uint32_t FifoDataCount; /* Count how many times impedance have been measured */
/* End */
}AppAMPCfg_Type;
/**
* int32_t type Impedance result in Cartesian coordinate
*/
typedef struct
{
float Current;
float Voltage;
}fAmpRes_Type;
#define AMPCTRL_START 0
#define AMPCTRL_STOPNOW 1
#define AMPCTRL_STOPSYNC 2
#define AMPCTRL_SHUTDOWN 4 /* Note: shutdown here means turn off everything and put AFE to hibernate mode. The word 'SHUT DOWN' is only used here. */
AD5940Err AppAMPGetCfg(void *pCfg);
AD5940Err AppAMPInit(uint32_t *pBuffer, uint32_t BufferSize);
AD5940Err AppAMPISR(void *pBuff, uint32_t *pCount);
AD5940Err AppAMPCtrl(int32_t AmpCtrl, void *pPara);
float AppAMPCalcVoltage(uint32_t ADCcode);
float AppAMPCalcCurrent(uint32_t ADCcode);
#endif

View File

@ -15,6 +15,8 @@ add_executable(EIS
main.c
ad5940.c
Impedance.c
Amperometric.c
RampTest.c
)
target_compile_definitions(EIS PRIVATE CHIPSEL_594X)

View File

@ -1,10 +1,12 @@
// File: Impedance.c
// Impedance.c
#include "ad5940.h"
#include <stdio.h>
#include "string.h"
#include "math.h"
#include "Impedance.h"
#define AD5940ERR_STOP 10
/* Default LPDAC resolution (2.5V internal reference) */
#define DAC12BITVOLT_1LSB (2200.0f/4095) // mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) // mV
@ -622,7 +624,7 @@ int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
{
AD5940_WUPTCtrl(bFALSE);
AD5940_SEQCtrlS(bFALSE);
return AD5940ERR_OK;
return AD5940ERR_STOP; // Return STOP code
}
}
if(AppIMPCfg.StopRequired == bTRUE)
@ -752,7 +754,8 @@ int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
{
AD5940_SEQCtrlS(bFALSE);
if (AppIMPRegModify(pBuff, &FifoCnt) == AD5940ERR_OK)
int32_t status = AppIMPRegModify(pBuff, &FifoCnt);
if (status == AD5940ERR_OK)
{
if(AppIMPCfg.FifoDataCount < AppIMPCfg.NumOfData || AppIMPCfg.NumOfData == -1)
{
@ -780,6 +783,8 @@ int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
} else {
*pCount = FifoCnt;
}
if (status == AD5940ERR_STOP) return AD5940ERR_STOP;
}
else
{

583
RampTest.c Normal file
View File

@ -0,0 +1,583 @@
// RampTest.c
#include "ad5940.h"
#include <stdio.h>
#include "string.h"
#include "math.h"
#include "RampTest.h"
#define AD5940ERR_STOP 10
AppRAMPCfg_Type AppRAMPCfg =
{
.bParaChanged = bFALSE,
.SeqStartAddr = 0,
.MaxSeqLen = 0,
.SeqStartAddrCal = 0,
.MaxSeqLenCal = 0,
.LFOSCClkFreq = 32000.0,
.SysClkFreq = 16000000.0,
.AdcClkFreq = 16000000.0,
.RcalVal = 10000.0,
.ADCRefVolt = 1820.0f,
.bTestFinished = bFALSE,
.RampStartVolt = -500.0f,
.RampPeakVolt = +500.0f,
.VzeroStart = 1100.0f,
.VzeroPeak = 1100.0f,
.StepNumber = 100,
.RampDuration = 10000,
.SampleDelay = 1.0f,
.LPTIARtiaSel = LPTIARTIA_4K,
.LpTiaRf = LPTIARF_20K, /* Default LPF */
.ExternalRtiaValue = 20000.0f,
.AdcPgaGain = ADCPGA_1P5,
.ADCSinc3Osr = ADCSINC3OSR_2,
.FifoThresh = 4,
.RAMPInited = bFALSE,
.StopRequired = bFALSE,
.RampState = RAMP_STATE0,
.bFirstDACSeq = bTRUE,
.bRampOneDir = bFALSE,
};
AD5940Err AppRAMPGetCfg(void *pCfg)
{
if(pCfg)
{
*(AppRAMPCfg_Type **)pCfg = &AppRAMPCfg;
return AD5940ERR_OK;
}
return AD5940ERR_PARA;
}
AD5940Err AppRAMPCtrl(uint32_t Command, void *pPara)
{
switch (Command)
{
case APPCTRL_START:
{
WUPTCfg_Type wupt_cfg;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
if(AppRAMPCfg.RAMPInited == bFALSE) return AD5940ERR_APPERROR;
if(AppRAMPCfg.RampState == RAMP_STOP) return AD5940ERR_APPERROR;
wupt_cfg.WuptEn = bTRUE;
wupt_cfg.WuptEndSeq = WUPTENDSEQ_D;
wupt_cfg.WuptOrder[0] = SEQID_0;
wupt_cfg.WuptOrder[1] = SEQID_2;
wupt_cfg.WuptOrder[2] = SEQID_1;
wupt_cfg.WuptOrder[3] = SEQID_2;
wupt_cfg.SeqxSleepTime[SEQID_2] = 4;
wupt_cfg.SeqxWakeupTime[SEQID_2] = (uint32_t)(AppRAMPCfg.LFOSCClkFreq * AppRAMPCfg.SampleDelay / 1000.0f) - 4 - 2;
wupt_cfg.SeqxSleepTime[SEQID_0] = 4;
wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppRAMPCfg.LFOSCClkFreq * (AppRAMPCfg.RampDuration / AppRAMPCfg.StepNumber - AppRAMPCfg.SampleDelay) / 1000.0f) - 4 - 2;
wupt_cfg.SeqxSleepTime[SEQID_1] = wupt_cfg.SeqxSleepTime[SEQID_0];
wupt_cfg.SeqxWakeupTime[SEQID_1] = wupt_cfg.SeqxWakeupTime[SEQID_0];
AD5940_WUPTCfg(&wupt_cfg);
break;
}
case APPCTRL_STOPNOW:
{
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
AD5940_WUPTCtrl(bFALSE);
AD5940_WUPTCtrl(bFALSE);
break;
}
case APPCTRL_STOPSYNC:
{
AppRAMPCfg.StopRequired = bTRUE;
break;
}
case APPCTRL_SHUTDOWN:
{
AppRAMPCtrl(APPCTRL_STOPNOW, 0);
AD5940_ShutDownS();
break;
}
default:
break;
}
return AD5940ERR_OK;
}
static AD5940Err AppRAMPSeqInitGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
LPLoopCfg_Type lploop_cfg;
DSPCfg_Type dsp_cfg;
AD5940_SEQGenCtrl(bTRUE);
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
aferef_cfg.HpBandgapEn = bTRUE;
aferef_cfg.Hp1V1BuffEn = bTRUE;
aferef_cfg.Hp1V8BuffEn = bTRUE;
aferef_cfg.Disc1V1Cap = bFALSE;
aferef_cfg.Disc1V8Cap = bFALSE;
aferef_cfg.Hp1V8ThemBuff = bFALSE;
aferef_cfg.Hp1V8Ilimit = bFALSE;
aferef_cfg.Lp1V1BuffEn = bTRUE;
aferef_cfg.Lp1V8BuffEn = bTRUE;
aferef_cfg.LpBandgapEn = bTRUE;
aferef_cfg.LpRefBufEn = bTRUE;
aferef_cfg.LpRefBoostEn = bFALSE;
AD5940_REFCfgS(&aferef_cfg);
lploop_cfg.LpAmpCfg.LpAmpSel = LPAMP0;
lploop_cfg.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
lploop_cfg.LpAmpCfg.LpPaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaRf = AppRAMPCfg.LpTiaRf; // Use Configured LPF
lploop_cfg.LpAmpCfg.LpTiaRload = LPTIARLOAD_10R;
lploop_cfg.LpAmpCfg.LpTiaRtia = AppRAMPCfg.LPTIARtiaSel;
lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
lploop_cfg.LpDacCfg.LpdacSel = LPDAC0;
lploop_cfg.LpDacCfg.DacData12Bit = 0x800;
lploop_cfg.LpDacCfg.DacData6Bit = 0;
lploop_cfg.LpDacCfg.DataRst = bFALSE;
lploop_cfg.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VBIAS2PIN|LPDACSW_VZERO2LPTIA|LPDACSW_VZERO2PIN;
lploop_cfg.LpDacCfg.LpDacRef = LPDACREF_2P5;
lploop_cfg.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
lploop_cfg.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
lploop_cfg.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
lploop_cfg.LpDacCfg.PowerEn = bTRUE;
AD5940_LPLoopCfgS(&lploop_cfg);
AD5940_StructInit(&dsp_cfg, sizeof(dsp_cfg));
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_LPTIA0_N;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_LPTIA0_P;
dsp_cfg.ADCBaseCfg.ADCPga = AppRAMPCfg.AdcPgaGain;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppRAMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ;
dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = ADCSINC2OSR_1067;
dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_2;
AD5940_DSPCfgS(&dsp_cfg);
AD5940_SEQGenInsert(SEQ_STOP());
AD5940_SEQGenCtrl(bFALSE);
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
if(error == AD5940ERR_OK)
{
AD5940_StructInit(&AppRAMPCfg.InitSeqInfo, sizeof(AppRAMPCfg.InitSeqInfo));
if(SeqLen >= AppRAMPCfg.MaxSeqLen) return AD5940ERR_SEQLEN;
AppRAMPCfg.InitSeqInfo.SeqId = SEQID_3;
AppRAMPCfg.InitSeqInfo.SeqRamAddr = AppRAMPCfg.SeqStartAddr;
AppRAMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppRAMPCfg.InitSeqInfo.SeqLen = SeqLen;
AppRAMPCfg.InitSeqInfo.WriteSRAM = bTRUE;
AD5940_SEQInfoCfg(&AppRAMPCfg.InitSeqInfo);
}
else
return error;
return AD5940ERR_OK;
}
static AD5940Err AppRAMPSeqADCCtrlGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
uint32_t WaitClks;
ClksCalInfo_Type clks_cal;
clks_cal.DataCount = 1;
clks_cal.DataType = DATATYPE_SINC3;
clks_cal.ADCSinc3Osr = AppRAMPCfg.ADCSinc3Osr;
clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067;
clks_cal.ADCAvgNum = ADCAVGNUM_2;
clks_cal.RatioSys2AdcClk = AppRAMPCfg.SysClkFreq / AppRAMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
AD5940_SEQGenCtrl(bTRUE);
AD5940_SEQGpioCtrlS(AGPIO_Pin2);
AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16 * 250));
AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks));
AD5940_AFECtrlS(AFECTRL_ADCPWR | AFECTRL_ADCCNV, bFALSE);
AD5940_SEQGpioCtrlS(0);
AD5940_EnterSleepS();
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE);
if(error == AD5940ERR_OK)
{
AD5940_StructInit(&AppRAMPCfg.ADCSeqInfo, sizeof(AppRAMPCfg.ADCSeqInfo));
if((SeqLen + AppRAMPCfg.InitSeqInfo.SeqLen) >= AppRAMPCfg.MaxSeqLen)
return AD5940ERR_SEQLEN;
AppRAMPCfg.ADCSeqInfo.SeqId = SEQID_2;
AppRAMPCfg.ADCSeqInfo.SeqRamAddr = AppRAMPCfg.InitSeqInfo.SeqRamAddr + AppRAMPCfg.InitSeqInfo.SeqLen ;
AppRAMPCfg.ADCSeqInfo.pSeqCmd = pSeqCmd;
AppRAMPCfg.ADCSeqInfo.SeqLen = SeqLen;
AppRAMPCfg.ADCSeqInfo.WriteSRAM = bTRUE;
AD5940_SEQInfoCfg(&AppRAMPCfg.ADCSeqInfo);
}
else
return error;
return AD5940ERR_OK;
}
static AD5940Err RampDacRegUpdate(uint32_t *pDACData)
{
uint32_t VbiasCode, VzeroCode;
if (AppRAMPCfg.bRampOneDir)
{
switch(AppRAMPCfg.RampState)
{
case RAMP_STATE0:
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
AppRAMPCfg.RampState = RAMP_STATE1;
break;
case RAMP_STATE1:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber / 2)
{
AppRAMPCfg.RampState = RAMP_STATE4;
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroPeak - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE4:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber)
AppRAMPCfg.RampState = RAMP_STOP;
break;
case RAMP_STOP:
break;
}
}
else
{
switch(AppRAMPCfg.RampState)
{
case RAMP_STATE0:
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
AppRAMPCfg.RampState = RAMP_STATE1;
break;
case RAMP_STATE1:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber / 4)
{
AppRAMPCfg.RampState = RAMP_STATE2;
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroPeak - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE2:
if(AppRAMPCfg.CurrStepPos >= (AppRAMPCfg.StepNumber * 2) / 4)
{
AppRAMPCfg.RampState = RAMP_STATE3;
AppRAMPCfg.bDACCodeInc = AppRAMPCfg.bDACCodeInc ? bFALSE : bTRUE;
}
break;
case RAMP_STATE3:
if(AppRAMPCfg.CurrStepPos >= (AppRAMPCfg.StepNumber * 3) / 4)
{
AppRAMPCfg.RampState = RAMP_STATE4;
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE4:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber)
AppRAMPCfg.RampState = RAMP_STOP;
break;
case RAMP_STOP:
break;
}
}
AppRAMPCfg.CurrStepPos ++;
if(AppRAMPCfg.bDACCodeInc)
AppRAMPCfg.CurrRampCode += AppRAMPCfg.DACCodePerStep;
else
AppRAMPCfg.CurrRampCode -= AppRAMPCfg.DACCodePerStep;
VzeroCode = AppRAMPCfg.CurrVzeroCode;
VbiasCode = (uint32_t)(VzeroCode * 64 + AppRAMPCfg.CurrRampCode);
if(VbiasCode < (VzeroCode * 64))
VbiasCode --;
if(VbiasCode > 4095) VbiasCode = 4095;
if(VzeroCode > 63) VzeroCode = 63;
*pDACData = (VzeroCode << 12) | VbiasCode;
return AD5940ERR_OK;
}
static AD5940Err AppRAMPSeqDACCtrlGen(void)
{
#define SEQLEN_ONESTEP 4L
#define CURRBLK_BLK0 0
#define CURRBLK_BLK1 1
AD5940Err error = AD5940ERR_OK;
uint32_t BlockStartSRAMAddr;
uint32_t DACData, SRAMAddr;
uint32_t i;
uint32_t StepsThisBlock;
BoolFlag bIsFinalBlk;
uint32_t SeqCmdBuff[SEQLEN_ONESTEP];
static BoolFlag bCmdForSeq0 = bTRUE;
static uint32_t DACSeqBlk0Addr, DACSeqBlk1Addr;
static uint32_t StepsRemainning, StepsPerBlock, DACSeqCurrBlk;
if(AppRAMPCfg.bFirstDACSeq == bTRUE)
{
int32_t DACSeqLenMax;
StepsRemainning = AppRAMPCfg.StepNumber;
DACSeqLenMax = (int32_t)AppRAMPCfg.MaxSeqLen - (int32_t)AppRAMPCfg.InitSeqInfo.SeqLen - (int32_t)AppRAMPCfg.ADCSeqInfo.SeqLen;
if(DACSeqLenMax < SEQLEN_ONESTEP * 4) return AD5940ERR_SEQLEN;
DACSeqLenMax -= SEQLEN_ONESTEP * 2;
StepsPerBlock = DACSeqLenMax / SEQLEN_ONESTEP / 2;
DACSeqBlk0Addr = AppRAMPCfg.ADCSeqInfo.SeqRamAddr + AppRAMPCfg.ADCSeqInfo.SeqLen;
DACSeqBlk1Addr = DACSeqBlk0Addr + StepsPerBlock * SEQLEN_ONESTEP;
DACSeqCurrBlk = CURRBLK_BLK0;
if (AppRAMPCfg.bRampOneDir)
{
AppRAMPCfg.DACCodePerStep = ((AppRAMPCfg.RampPeakVolt - AppRAMPCfg.RampStartVolt) / AppRAMPCfg.StepNumber) / DAC12BITVOLT_1LSB;
}
else
{
AppRAMPCfg.DACCodePerStep = ((AppRAMPCfg.RampPeakVolt - AppRAMPCfg.RampStartVolt) / AppRAMPCfg.StepNumber * 2) / DAC12BITVOLT_1LSB;
}
if(AppRAMPCfg.DACCodePerStep > 0)
AppRAMPCfg.bDACCodeInc = bTRUE;
else
{
AppRAMPCfg.DACCodePerStep = -AppRAMPCfg.DACCodePerStep;
AppRAMPCfg.bDACCodeInc = bFALSE;
}
AppRAMPCfg.CurrRampCode = AppRAMPCfg.RampStartVolt / DAC12BITVOLT_1LSB;
AppRAMPCfg.RampState = RAMP_STATE0;
AppRAMPCfg.CurrStepPos = 0;
bCmdForSeq0 = bTRUE;
}
if(StepsRemainning == 0) return AD5940ERR_OK;
bIsFinalBlk = StepsRemainning <= StepsPerBlock ? bTRUE : bFALSE;
if(bIsFinalBlk)
StepsThisBlock = StepsRemainning;
else
StepsThisBlock = StepsPerBlock;
StepsRemainning -= StepsThisBlock;
BlockStartSRAMAddr = (DACSeqCurrBlk == CURRBLK_BLK0) ? DACSeqBlk0Addr : DACSeqBlk1Addr;
SRAMAddr = BlockStartSRAMAddr;
for(i = 0; i < StepsThisBlock - 1; i++)
{
uint32_t CurrAddr = SRAMAddr;
SRAMAddr += SEQLEN_ONESTEP;
RampDacRegUpdate(&DACData);
SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
SeqCmdBuff[1] = SEQ_WAIT(10);
SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0 ? REG_AFE_SEQ1INFO : REG_AFE_SEQ0INFO, (SRAMAddr << BITP_AFE_SEQ1INFO_ADDR) | (SEQLEN_ONESTEP << BITP_AFE_SEQ1INFO_LEN));
SeqCmdBuff[3] = SEQ_SLP();
AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
bCmdForSeq0 = bCmdForSeq0 ? bFALSE : bTRUE;
}
if(bIsFinalBlk)
{
uint32_t CurrAddr = SRAMAddr;
SRAMAddr += SEQLEN_ONESTEP;
RampDacRegUpdate(&DACData);
SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
SeqCmdBuff[1] = SEQ_WAIT(10);
SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0 ? REG_AFE_SEQ1INFO : REG_AFE_SEQ0INFO, (SRAMAddr << BITP_AFE_SEQ1INFO_ADDR) | (SEQLEN_ONESTEP << BITP_AFE_SEQ1INFO_LEN));
SeqCmdBuff[3] = SEQ_SLP();
AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
CurrAddr += SEQLEN_ONESTEP;
SeqCmdBuff[0] = SEQ_NOP();
SeqCmdBuff[1] = SEQ_NOP();
SeqCmdBuff[2] = SEQ_NOP();
SeqCmdBuff[3] = SEQ_STOP();
AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
}
else
{
uint32_t CurrAddr = SRAMAddr;
SRAMAddr = (DACSeqCurrBlk == CURRBLK_BLK0) ? DACSeqBlk1Addr : DACSeqBlk0Addr;
RampDacRegUpdate(&DACData);
SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
SeqCmdBuff[1] = SEQ_WAIT(10);
SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0 ? REG_AFE_SEQ1INFO : REG_AFE_SEQ0INFO, (SRAMAddr << BITP_AFE_SEQ1INFO_ADDR) | (SEQLEN_ONESTEP << BITP_AFE_SEQ1INFO_LEN));
SeqCmdBuff[3] = SEQ_INT0();
AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
bCmdForSeq0 = bCmdForSeq0 ? bFALSE : bTRUE;
}
DACSeqCurrBlk = (DACSeqCurrBlk == CURRBLK_BLK0) ? CURRBLK_BLK1 : CURRBLK_BLK0;
if(AppRAMPCfg.bFirstDACSeq)
{
AppRAMPCfg.bFirstDACSeq = bFALSE;
if(bIsFinalBlk == bFALSE)
{
error = AppRAMPSeqDACCtrlGen();
if(error != AD5940ERR_OK) return error;
}
AppRAMPCfg.DACSeqInfo.SeqId = SEQID_0;
AppRAMPCfg.DACSeqInfo.SeqLen = SEQLEN_ONESTEP;
AppRAMPCfg.DACSeqInfo.SeqRamAddr = BlockStartSRAMAddr;
AppRAMPCfg.DACSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.DACSeqInfo);
}
return AD5940ERR_OK;
}
AD5940Err AppRAMPInit(uint32_t *pBuffer, uint32_t BufferSize)
{
AD5940Err error = AD5940ERR_OK;
FIFOCfg_Type fifo_cfg;
SEQCfg_Type seq_cfg;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
seq_cfg.SeqMemSize = SEQMEMSIZE_4KB;
seq_cfg.SeqBreakEn = bFALSE;
seq_cfg.SeqIgnoreEn = bFALSE;
seq_cfg.SeqCntCRCClr = bTRUE;
seq_cfg.SeqEnable = bFALSE;
seq_cfg.SeqWrTimer = 0;
AD5940_SEQCfg(&seq_cfg);
if((AppRAMPCfg.RAMPInited == bFALSE) || (AppRAMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
AppRAMPCfg.RAMPInited = bFALSE;
AD5940_SEQGenInit(pBuffer, BufferSize);
error = AppRAMPSeqInitGen();
if(error != AD5940ERR_OK) return error;
error = AppRAMPSeqADCCtrlGen();
if(error != AD5940ERR_OK) return error;
AppRAMPCfg.bParaChanged = bFALSE;
}
AD5940_FIFOCtrlS(FIFOSRC_SINC3, bFALSE);
fifo_cfg.FIFOEn = bTRUE;
fifo_cfg.FIFOSrc = FIFOSRC_SINC3;
fifo_cfg.FIFOThresh = AppRAMPCfg.FifoThresh;
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
fifo_cfg.FIFOSize = FIFOSIZE_2KB;
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
AppRAMPCfg.bFirstDACSeq = bTRUE;
error = AppRAMPSeqDACCtrlGen();
if(error != AD5940ERR_OK) return error;
AppRAMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.InitSeqInfo);
AD5940_SEQCtrlS(bTRUE);
AD5940_SEQMmrTrig(AppRAMPCfg.InitSeqInfo.SeqId);
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ);
AppRAMPCfg.ADCSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.ADCSeqInfo);
AppRAMPCfg.DACSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.DACSeqInfo);
AD5940_SEQCtrlS(bFALSE);
AD5940_WriteReg(REG_AFE_SEQCNT, 0);
AD5940_SEQCtrlS(bTRUE);
AD5940_ClrMCUIntFlag();
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
AppRAMPCfg.RAMPInited = bTRUE;
return AD5940ERR_OK;
}
static int32_t AppRAMPRegModify(int32_t *const pData, uint32_t *pDataCount)
{
if(AppRAMPCfg.StopRequired == bTRUE)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
return AD5940ERR_OK;
}
static int32_t AppRAMPDataProcess(int32_t *const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
float *pOut = (float *)pData;
float temp;
for(i = 0; i < datacount; i++)
{
pData[i] &= 0xffff;
temp = -AD5940_ADCCode2Volt(pData[i], AppRAMPCfg.AdcPgaGain, AppRAMPCfg.ADCRefVolt);
pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */
}
return 0;
}
AD5940Err AppRAMPISR(void *pBuff, uint32_t *pCount)
{
uint32_t BuffCount;
uint32_t FifoCnt;
BuffCount = *pCount;
uint32_t IntFlag;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
AD5940_SleepKeyCtrlS(SLPKEY_LOCK);
*pCount = 0;
IntFlag = AD5940_INTCGetFlag(AFEINTC_0);
if(IntFlag & AFEINTSRC_CUSTOMINT0)
{
AD5940Err error;
AD5940_INTCClrFlag(AFEINTSRC_CUSTOMINT0);
error = AppRAMPSeqDACCtrlGen();
if(error != AD5940ERR_OK) return error;
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
}
if(IntFlag & AFEINTSRC_DATAFIFOTHRESH)
{
FifoCnt = AD5940_FIFOGetCnt();
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AppRAMPRegModify((int32_t*)pBuff, &FifoCnt);
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
AppRAMPDataProcess((int32_t *)pBuff, &FifoCnt);
*pCount = FifoCnt;
return 0;
}
if(IntFlag & AFEINTSRC_ENDSEQ)
{
FifoCnt = AD5940_FIFOGetCnt();
AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ);
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AppRAMPDataProcess((int32_t *)pBuff, &FifoCnt);
*pCount = FifoCnt;
AppRAMPCtrl(APPCTRL_STOPNOW, 0);
// Signal that we are done
return AD5940ERR_STOP;
}
return 0;
}

66
RampTest.h Normal file
View File

@ -0,0 +1,66 @@
// RampTest.h
#ifndef _RAMPTEST_H_
#define _RAMPTEST_H_
#include "ad5940.h"
#include <stdio.h>
#include "string.h"
#include "math.h"
#define ALIGIN_VOLT2LSB 0
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
typedef struct
{
BoolFlag bParaChanged;
uint32_t SeqStartAddr;
uint32_t MaxSeqLen;
uint32_t SeqStartAddrCal;
uint32_t MaxSeqLenCal;
float LFOSCClkFreq;
float SysClkFreq;
float AdcClkFreq;
float RcalVal;
float ADCRefVolt;
BoolFlag bTestFinished;
float RampStartVolt;
float RampPeakVolt;
float VzeroStart;
float VzeroPeak;
uint32_t StepNumber;
uint32_t RampDuration;
float SampleDelay;
uint32_t LPTIARtiaSel;
uint32_t LPTIARloadSel;
uint32_t LpTiaRf; /* Added LPF Resistor Configuration */
float ExternalRtiaValue;
uint32_t AdcPgaGain;
uint8_t ADCSinc3Osr;
uint32_t FifoThresh;
BoolFlag RAMPInited;
fImpPol_Type RtiaValue;
SEQInfo_Type InitSeqInfo;
SEQInfo_Type ADCSeqInfo;
BoolFlag bFirstDACSeq;
SEQInfo_Type DACSeqInfo;
uint32_t CurrStepPos;
float DACCodePerStep;
float CurrRampCode;
uint32_t CurrVzeroCode;
BoolFlag bDACCodeInc;
BoolFlag StopRequired;
enum _RampState{RAMP_STATE0 = 0, RAMP_STATE1, RAMP_STATE2, RAMP_STATE3, RAMP_STATE4, RAMP_STOP} RampState;
BoolFlag bRampOneDir;
}AppRAMPCfg_Type;
#define APPCTRL_START 0
#define APPCTRL_STOPNOW 1
#define APPCTRL_STOPSYNC 2
#define APPCTRL_SHUTDOWN 3
AD5940Err AppRAMPInit(uint32_t *pBuffer, uint32_t BufferSize);
AD5940Err AppRAMPGetCfg(void *pCfg);
AD5940Err AppRAMPISR(void *pBuff, uint32_t *pCount);
AD5940Err AppRAMPCtrl(uint32_t Command, void *pPara);
#endif

View File

@ -1,4 +1,4 @@
// File: host/src/GraphWidget.cpp
// host/src/GraphWidget.cpp
#include "GraphWidget.h"
#include <limits>
#include <cmath>
@ -7,159 +7,322 @@ GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent) {
layout = new QVBoxLayout(this);
plot = new QCustomPlot(this);
// Setup Layout
layout->addWidget(plot);
layout->setContentsMargins(0, 0, 0, 0);
// Setup Graphs
graph1 = plot->addGraph();
graph2 = plot->addGraph(plot->xAxis, plot->yAxis2);
graph3 = plot->addGraph(plot->xAxis, plot->yAxis2); // Hilbert Trace
plot->setBackground(QBrush(QColor(25, 25, 25)));
// Style Graph 1 (Real - Blue)
QPen pen1(Qt::blue);
auto styleAxis = [](QCPAxis *axis) {
axis->setBasePen(QPen(Qt::white));
axis->setTickPen(QPen(Qt::white));
axis->setSubTickPen(QPen(Qt::white));
axis->setTickLabelColor(Qt::white);
axis->setLabelColor(Qt::white);
axis->grid()->setPen(QPen(QColor(60, 60, 60), 0, Qt::DotLine));
axis->grid()->setSubGridVisible(true);
axis->grid()->setSubGridPen(QPen(QColor(40, 40, 40), 0, Qt::DotLine));
};
styleAxis(plot->xAxis);
styleAxis(plot->yAxis);
styleAxis(plot->yAxis2);
// --- Setup Graphs ---
// 1. Real / Raw Nyquist (Cyan)
graphReal = plot->addGraph();
QPen pen1(QColor(0, 255, 255));
pen1.setWidth(2);
graph1->setPen(pen1);
graph1->setLineStyle(QCPGraph::lsLine);
graph1->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
graph1->setName("Real");
graphReal->setPen(pen1);
graphReal->setLineStyle(QCPGraph::lsLine);
graphReal->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QColor(0, 255, 255), 3));
// Style Graph 2 (Imaginary - Red)
QPen pen2(Qt::red);
// 2. Imaginary (Magenta)
graphImag = plot->addGraph(plot->xAxis, plot->yAxis2);
QPen pen2(QColor(255, 0, 255));
pen2.setWidth(2);
graph2->setPen(pen2);
graph2->setLineStyle(QCPGraph::lsLine);
graph2->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, 3));
graph2->setName("Imaginary");
graphImag->setPen(pen2);
graphImag->setLineStyle(QCPGraph::lsLine);
graphImag->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, QColor(255, 0, 255), 3));
// Style Graph 3 (Hilbert - Green Dashed)
// 3. Hilbert (Green Dashed)
graphHilbert = plot->addGraph(plot->xAxis, plot->yAxis2);
QPen pen3(Qt::green);
pen3.setWidth(2);
pen3.setStyle(Qt::DashLine);
graph3->setPen(pen3);
graph3->setLineStyle(QCPGraph::lsLine);
graph3->setName("Hilbert (Analytic)");
graphHilbert->setPen(pen3);
// 4. Corrected Nyquist (Orange)
graphNyquistCorr = plot->addGraph(plot->xAxis, plot->yAxis);
QPen pen4(QColor(255, 165, 0));
pen4.setWidth(2);
graphNyquistCorr->setPen(pen4);
graphNyquistCorr->setLineStyle(QCPGraph::lsLine);
graphNyquistCorr->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, 4));
graphNyquistCorr->setName("De-embedded (True Cell)");
// 5. Amperometric Graph (Lime)
graphAmp = plot->addGraph();
QPen pen5(QColor(50, 255, 50));
pen5.setWidth(2);
graphAmp->setPen(pen5);
graphAmp->setLineStyle(QCPGraph::lsLine);
graphAmp->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 3));
graphAmp->setName("Current");
// 6. Extrapolated Point (Gold Star)
graphExtrapolated = plot->addGraph(plot->xAxis, plot->yAxis);
graphExtrapolated->setLineStyle(QCPGraph::lsNone);
graphExtrapolated->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssStar, QColor(255, 215, 0), 12));
QPen pen6(QColor(255, 215, 0));
pen6.setWidth(3);
graphExtrapolated->setPen(pen6);
graphExtrapolated->setName("Rs (Extrapolated)");
// 7. LSV Blank (Gray)
graphLSVBlank = plot->addGraph();
QPen penBlank(QColor(150, 150, 150));
penBlank.setWidth(2);
penBlank.setStyle(Qt::DashLine);
graphLSVBlank->setPen(penBlank);
graphLSVBlank->setName("Blank (Tap Water)");
// 8. LSV Sample (Yellow)
graphLSVSample = plot->addGraph();
QPen penSample(Qt::yellow);
penSample.setWidth(2);
graphLSVSample->setPen(penSample);
graphLSVSample->setName("Sample (Bleach)");
// 9. LSV Diff (Cyan)
graphLSVDiff = plot->addGraph();
QPen penDiff(Qt::cyan);
penDiff.setWidth(3);
graphLSVDiff->setPen(penDiff);
graphLSVDiff->setName("Diff (Chlorine)");
graphNyquistRaw = graphReal;
// 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);
// Legend
plot->legend->setVisible(true);
QFont legendFont = font();
legendFont.setPointSize(9);
plot->legend->setFont(legendFont);
plot->legend->setBrush(QBrush(QColor(255, 255, 255, 230)));
plot->legend->setBrush(QBrush(QColor(40, 40, 40, 200)));
plot->legend->setBorderPen(QPen(Qt::white));
plot->legend->setTextColor(Qt::white);
// Default Mode
configureRawPlot();
}
void GraphWidget::configureRawPlot() {
clear();
// X Axis: Frequency (Log)
// Only clear if explicitly requested, but here we just set visibility
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: Real (Linear)
plot->yAxis->setLabel("Real (Ohms)");
plot->yAxis->setScaleType(QCPAxis::stLinear);
QSharedPointer<QCPAxisTicker> linTicker(new QCPAxisTicker);
plot->yAxis->setTicker(linTicker);
plot->yAxis->setNumberFormat("f");
// Y Axis 2: Imaginary (Linear)
plot->yAxis2->setLabel("Imaginary (Ohms)");
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);
graphReal->setName("Real");
graphImag->setName("Imaginary");
graphHilbert->setName("Hilbert");
graphReal->setVisible(true);
graphImag->setVisible(true);
graphHilbert->setVisible(true);
graphNyquistCorr->setVisible(false);
graphAmp->setVisible(false);
graphExtrapolated->setVisible(false);
graphLSVBlank->setVisible(false);
graphLSVSample->setVisible(false);
graphLSVDiff->setVisible(false);
plot->replot();
}
void GraphWidget::configureNyquistPlot() {
clear();
// 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");
// 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));
graphReal->setName("Measured (Raw)");
graphReal->setLineStyle(QCPGraph::lsLine);
graphReal->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4));
graphReal->setVisible(true);
graphImag->setVisible(false);
graphHilbert->setVisible(false);
graphNyquistCorr->setVisible(true);
graphAmp->setVisible(false);
graphExtrapolated->setVisible(true);
graphLSVBlank->setVisible(false);
graphLSVSample->setVisible(false);
graphLSVDiff->setVisible(false);
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')");
void GraphWidget::configureAmperometricPlot() {
plot->xAxis->setLabel("Sample Index");
plot->xAxis->setScaleType(QCPAxis::stLinear);
QSharedPointer<QCPAxisTicker> linTicker(new QCPAxisTicker);
plot->xAxis->setTicker(linTicker);
plot->xAxis->setNumberFormat("f");
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;
plot->yAxis->setLabel("Current (uA)");
plot->yAxis->setScaleType(QCPAxis::stLinear);
plot->yAxis->setTicker(linTicker);
plot->yAxis->setNumberFormat("f");
graph1->addData(x, val1);
graph2->addData(x, val2);
}
plot->yAxis2->setVisible(false);
// Auto-scale
graph1->rescaleAxes(false);
if (!isNyquist) {
graph2->rescaleAxes(false);
graph3->rescaleAxes(false);
}
graphAmp->setName("Current");
graphAmp->setVisible(true);
graphReal->setVisible(false);
graphImag->setVisible(false);
graphHilbert->setVisible(false);
graphNyquistCorr->setVisible(false);
graphExtrapolated->setVisible(false);
graphLSVBlank->setVisible(false);
graphLSVSample->setVisible(false);
graphLSVDiff->setVisible(false);
plot->replot();
}
void GraphWidget::configureLSVPlot() {
plot->xAxis->setLabel("Voltage (mV)");
plot->xAxis->setScaleType(QCPAxis::stLinear);
QSharedPointer<QCPAxisTicker> linTicker(new QCPAxisTicker);
plot->xAxis->setTicker(linTicker);
plot->xAxis->setNumberFormat("f");
plot->yAxis->setLabel("Current (uA)");
plot->yAxis->setScaleType(QCPAxis::stLinear);
plot->yAxis->setTicker(linTicker);
plot->yAxis->setNumberFormat("f");
plot->yAxis2->setVisible(false);
graphLSVBlank->setVisible(true);
graphLSVSample->setVisible(true);
graphLSVDiff->setVisible(true);
graphReal->setVisible(false);
graphImag->setVisible(false);
graphHilbert->setVisible(false);
graphNyquistCorr->setVisible(false);
graphExtrapolated->setVisible(false);
graphAmp->setVisible(false);
plot->replot();
}
void GraphWidget::addBodeData(double freq, double val1, double val2) {
if (plot->xAxis->scaleType() == QCPAxis::stLogarithmic && freq <= 0) return;
graphReal->addData(freq, val1);
graphImag->addData(freq, val2);
graphReal->rescaleAxes(false);
graphImag->rescaleAxes(false);
graphHilbert->rescaleAxes(false);
plot->replot();
}
void GraphWidget::addNyquistData(double r_meas, double i_meas, double r_corr, double i_corr, bool showCorr) {
graphNyquistRaw->addData(r_meas, -i_meas);
if (showCorr) {
graphNyquistCorr->addData(r_corr, -i_corr);
}
graphNyquistRaw->rescaleAxes(false);
if (showCorr) {
graphNyquistCorr->rescaleAxes(true);
}
plot->replot();
}
void GraphWidget::addAmperometricData(double index, double current) {
graphAmp->addData(index, current);
graphAmp->rescaleAxes(false);
plot->replot();
}
void GraphWidget::addLSVData(double voltage, double current, LSVTrace traceType) {
QCPGraph* target = nullptr;
switch(traceType) {
case LSV_BLANK: target = graphLSVBlank; break;
case LSV_SAMPLE: target = graphLSVSample; break;
case LSV_DIFF: target = graphLSVDiff; break;
}
if (target) {
target->addData(voltage, current);
target->rescaleAxes(false); // Rescale to fit new data
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;
graphHilbert->setData(freq, hilbertImag);
graphHilbert->rescaleAxes(false);
plot->replot();
}
graph3->setData(freq, hilbertImag);
graph3->rescaleAxes(false);
void GraphWidget::setExtrapolatedPoint(double real, double imag) {
graphExtrapolated->data()->clear();
graphExtrapolated->addData(real, -imag);
plot->replot();
}
void GraphWidget::clear() {
graph1->data()->clear();
graph2->data()->clear();
graph3->data()->clear();
graphReal->data()->clear();
graphImag->data()->clear();
graphHilbert->data()->clear();
graphNyquistCorr->data()->clear();
graphAmp->data()->clear();
graphExtrapolated->data()->clear();
// Note: We deliberately do NOT clear LSV data in generic clear()
// to allow Blank scans to persist across tab changes or mode switches
// until explicitly cleared.
plot->replot();
}
void GraphWidget::clearLSV(LSVTrace traceType) {
if (traceType == LSV_BLANK) graphLSVBlank->data()->clear();
if (traceType == LSV_SAMPLE) graphLSVSample->data()->clear();
if (traceType == LSV_DIFF) graphLSVDiff->data()->clear();
plot->replot();
}

View File

@ -1,4 +1,4 @@
// File: host/src/GraphWidget.h
// host/src/GraphWidget.h
#pragma once
#include <QWidget>
@ -12,18 +12,46 @@ public:
explicit GraphWidget(QWidget *parent = nullptr);
// Data Handling
void addData(double freq, double val1, double val2);
void addBodeData(double freq, double val1, double val2);
void addNyquistData(double r_meas, double i_meas, double r_corr, double i_corr, bool showCorr);
void addAmperometricData(double index, double current);
// Updated LSV Data handler
enum LSVTrace { LSV_BLANK, LSV_SAMPLE, LSV_DIFF };
void addLSVData(double voltage, double current, LSVTrace traceType);
void addHilbertData(const QVector<double>& freq, const QVector<double>& hilbertImag);
void setExtrapolatedPoint(double real, double imag);
void clear();
void clearLSV(LSVTrace traceType); // Clear specific LSV trace
// View Configurations
void configureRawPlot();
void configureNyquistPlot();
void configureAmperometricPlot();
void configureLSVPlot();
private:
QVBoxLayout *layout;
QCustomPlot *plot;
QCPGraph *graph1; // Real
QCPGraph *graph2; // Imaginary
QCPGraph *graph3; // Hilbert (Analytic Imaginary)
// Bode Graphs
QCPGraph *graphReal;
QCPGraph *graphImag;
QCPGraph *graphHilbert;
// Nyquist Graphs
QCPGraph *graphNyquistRaw;
QCPGraph *graphNyquistCorr;
QCPGraph *graphExtrapolated;
// Amperometric Graph
QCPGraph *graphAmp;
// LSV Graphs
QCPGraph *graphLSVBlank; // The "Tap Water" baseline
QCPGraph *graphLSVSample; // The "Bleach" spike
QCPGraph *graphLSVDiff; // The calculated difference (Chlorine only)
};

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// File: host/src/MainWindow.h
// host/src/MainWindow.h
#pragma once
#include <QMainWindow>
@ -14,6 +14,8 @@
#include <QGestureEvent>
#include <QSwipeGesture>
#include <QTimer>
#include <QCheckBox>
#include <QSettings>
#include "GraphWidget.h"
class MainWindow : public QMainWindow {
@ -31,29 +33,49 @@ private slots:
void connectToPort();
void refreshPorts();
void onPortError(QSerialPort::SerialPortError error);
void onBlinkTimer();
// Action Slots
void checkDeviceId();
void runCalibration();
void startSweep();
void toggleMeasurement();
void toggleAmperometry();
// LSV Slots
void startLSVBlank();
void startLSVSample();
void stopLSV();
void calibrateCellConstant();
void onLPFChanged(int index);
private:
void setupUi();
void loadSettings();
void saveSettings();
void parseData(const QString &data);
void handleSwipe(QSwipeGesture *gesture);
void computeHilbert();
void performCircleFit();
void calculateLSVDiff();
void setButtonBlinking(QPushButton *btn, bool blinking);
QSerialPort *serial;
QSettings *settings;
QTimer *blinkTimer;
QPushButton *activeButton = nullptr;
bool blinkState = false;
// Views
GraphWidget *rawGraph; // Raw Data (Freq vs Real/Imag)
GraphWidget *nyquistGraph; // Nyquist (Real vs -Imag)
QTextEdit *logWidget; // Serial Log
GraphWidget *rawGraph;
GraphWidget *nyquistGraph;
GraphWidget *ampGraph;
GraphWidget *lsvGraph;
QTextEdit *logWidget;
// Layout
QTabWidget *tabWidget;
QToolBar *toolbar;
QComboBox *portSelector;
QPushButton *connectBtn;
QPushButton *checkIdBtn;
@ -62,14 +84,52 @@ private:
QPushButton *measureBtn;
QDoubleSpinBox *spinFreq;
// EIS
// EIS Configuration
QDoubleSpinBox *spinSweepStart;
QDoubleSpinBox *spinSweepStop;
QSpinBox *spinSweepPPD;
QComboBox *comboRange;
bool isMeasuring = false;
// Shunt / De-embedding Configuration
QCheckBox *checkShunt;
QDoubleSpinBox *spinShuntRes;
// Data Accumulation for Hilbert
// Amperometry Configuration
QDoubleSpinBox *spinAmpBias;
QPushButton *ampBtn;
QComboBox *comboLPF; // New LPF Dropdown
// LSV Configuration
QDoubleSpinBox *spinLsvStart;
QDoubleSpinBox *spinLsvStop;
QSpinBox *spinLsvSteps;
QSpinBox *spinLsvDuration;
QPushButton *lsvBlankBtn;
QPushButton *lsvSampleBtn;
// Conductivity Calibration
QDoubleSpinBox *spinCondStd;
QPushButton *btnCalCond;
QLabel *lblResultRs;
QLabel *lblResultCond;
double cellConstant = 1.0;
bool isMeasuringImp = false;
bool isMeasuringAmp = false;
bool isSweeping = false;
// LSV State
enum LSVState { LSV_IDLE, LSV_RUNNING_BLANK, LSV_RUNNING_SAMPLE };
LSVState lsvState = LSV_IDLE;
// Data Accumulation
QVector<double> sweepFreqs;
QVector<double> sweepReals;
QVector<double> sweepImags;
// LSV Data Storage for Diff Calculation
struct LSVPoint { double voltage; double current; };
QVector<LSVPoint> lsvBlankData;
QVector<LSVPoint> lsvSampleData;
};

View File

@ -1,23 +1,67 @@
// File: host/src/main.cpp
// host/src/main.cpp
#include <QApplication>
#include <QStyleFactory>
#include "MainWindow.h"
int main(int argc, char *argv[]) {
// High DPI Scaling
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication app(argc, argv);
QApplication::setApplicationName("EIS Configurator");
QApplication::setApplicationVersion("1.0");
// Dark Theme
QPalette p = app.palette();
p.setColor(QPalette::Window, QColor(30, 30, 30));
// --- Apply Dark Fusion Theme ---
app.setStyle(QStyleFactory::create("Fusion"));
QPalette p;
p.setColor(QPalette::Window, QColor(53, 53, 53));
p.setColor(QPalette::WindowText, Qt::white);
p.setColor(QPalette::Base, QColor(15, 15, 15));
p.setColor(QPalette::AlternateBase, QColor(45, 45, 45));
p.setColor(QPalette::Base, QColor(25, 25, 25));
p.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
p.setColor(QPalette::ToolTipBase, Qt::white);
p.setColor(QPalette::ToolTipText, Qt::white);
p.setColor(QPalette::Text, Qt::white);
p.setColor(QPalette::Button, QColor(45, 45, 45));
p.setColor(QPalette::Button, QColor(53, 53, 53));
p.setColor(QPalette::ButtonText, Qt::white);
p.setColor(QPalette::BrightText, Qt::red);
p.setColor(QPalette::Link, QColor(42, 130, 218));
p.setColor(QPalette::Highlight, QColor(42, 130, 218));
p.setColor(QPalette::HighlightedText, Qt::black);
app.setPalette(p);
// --- Global Stylesheet for Modern Look ---
app.setStyleSheet(
"QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }"
"QGroupBox { border: 1px solid #555; border-radius: 5px; margin-top: 10px; font-weight: bold; }"
"QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; }"
"QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit { "
" background: #333; color: #FFF; border: 1px solid #555; padding: 4px; border-radius: 4px; "
" selection-background-color: #2A82DA; "
"}"
"QSpinBox::up-button, QDoubleSpinBox::up-button, QSpinBox::down-button, QDoubleSpinBox::down-button { "
" background: #444; width: 16px; border-radius: 2px;"
"}"
"QPushButton { "
" background-color: #444; border: 1px solid #555; border-radius: 4px; padding: 5px 15px; color: white; font-weight: bold;"
"}"
"QPushButton:hover { background-color: #555; }"
"QPushButton:pressed { background-color: #2A82DA; }"
"QPushButton:disabled { background-color: #333; color: #777; border: 1px solid #444; }"
"QTabWidget::pane { border: 1px solid #444; }"
"QTabBar::tab { background: #333; color: #AAA; padding: 8px 20px; border-top-left-radius: 4px; border-top-right-radius: 4px; }"
"QTabBar::tab:selected { background: #535353; color: #FFF; border-bottom: 2px solid #2A82DA; }"
"QScrollBar:vertical { background: #333; width: 12px; }"
"QScrollBar::handle:vertical { background: #555; min-height: 20px; border-radius: 6px; }"
"QLabel { color: #EEE; }"
);
// Set default font size
QFont font = app.font();
font.setPointSize(12);
app.setFont(font);
MainWindow w;
w.show();

357
main.c
View File

@ -9,6 +9,15 @@
#include "hardware/watchdog.h"
#include "ad5940.h"
#include "Impedance.h"
#include "Amperometric.h"
#include "RampTest.h"
#define AD5940ERR_STOP 10
// Fix for missing definition in some SDK versions
#ifndef LPTIARF_BYPASS
#define LPTIARF_BYPASS 0x2000
#endif
// ---------------------------------------------------------------------------
// Hardware Definitions
@ -23,6 +32,72 @@
#define APPBUFF_SIZE 512
uint32_t AppBuff[APPBUFF_SIZE];
// Application State
typedef enum {
MODE_IDLE,
MODE_IMPEDANCE,
MODE_AMPEROMETRIC,
MODE_RAMP
} AppMode;
AppMode CurrentMode = MODE_IDLE;
float LFOSCFreq = 32000.0; // Default, updated by calibration
// ---------------------------------------------------------------------------
// Range / RTIA Management
// ---------------------------------------------------------------------------
// Nominal values for the 8 supported ranges
float RtiaCalibrationTable[8] = {200.0, 1000.0, 5000.0, 10000.0, 20000.0, 40000.0, 80000.0, 160000.0};
uint32_t CurrentRtiaIndex = 0; // Default to 200 Ohm (Index 0)
uint32_t ConfigRtiaVal = 200; // Default Value
uint32_t CurrentLpTiaRf = LPTIARF_20K; // Default LPF
// Map integer value to HSTIA Enum (Impedance)
uint32_t GetHSTIARtia(uint32_t val) {
switch(val) {
case 200: return HSTIARTIA_200;
case 1000: return HSTIARTIA_1K;
case 5000: return HSTIARTIA_5K;
case 10000: return HSTIARTIA_10K;
case 20000: return HSTIARTIA_20K;
case 40000: return HSTIARTIA_40K;
case 80000: return HSTIARTIA_80K;
case 160000: return HSTIARTIA_160K;
default: return HSTIARTIA_200;
}
}
// Map integer value to LPTIA Enum (Amperometric/Ramp)
uint32_t GetLPTIARtia(uint32_t val) {
switch(val) {
case 200: return LPTIARTIA_200R;
case 1000: return LPTIARTIA_1K;
case 5000: return LPTIARTIA_4K;
case 10000: return LPTIARTIA_10K;
case 20000: return LPTIARTIA_20K;
case 40000: return LPTIARTIA_40K;
case 80000: return LPTIARTIA_85K;
case 160000: return LPTIARTIA_160K;
default: return LPTIARTIA_1K;
}
}
// Helper to find index for calibration table
int GetRtiaIndex(uint32_t val) {
switch(val) {
case 200: return 0;
case 1000: return 1;
case 5000: return 2;
case 10000: return 3;
case 20000: return 4;
case 40000: return 5;
case 80000: return 6;
case 160000: return 7;
default: return 0;
}
}
// ---------------------------------------------------------------------------
// Platform Interface Implementation
// ---------------------------------------------------------------------------
@ -77,41 +152,80 @@ void setup_pins(void) {
void AD5940ImpedanceStructInit(void)
{
AppIMPCfg_Type *pImpedanceCfg;
AppIMPGetCfg(&pImpedanceCfg);
pImpedanceCfg->SeqStartAddr = 0;
pImpedanceCfg->MaxSeqLen = 512;
pImpedanceCfg->RcalVal = 100.0;
pImpedanceCfg->RtiaVal = 200.0;
pImpedanceCfg->RtiaVal = RtiaCalibrationTable[CurrentRtiaIndex];
pImpedanceCfg->SinFreq = 1000.0;
pImpedanceCfg->FifoThresh = 6;
pImpedanceCfg->DacVoltPP = 600.0;
pImpedanceCfg->ExcitBufGain = EXCITBUFGAIN_0P25;
pImpedanceCfg->HsDacGain = HSDACGAIN_0P2;
pImpedanceCfg->DswitchSel = SWD_CE0;
pImpedanceCfg->PswitchSel = SWP_CE0;
pImpedanceCfg->NswitchSel = SWN_SE0;
pImpedanceCfg->TswitchSel = SWT_SE0LOAD;
pImpedanceCfg->HstiaRtiaSel = HSTIARTIA_200;
pImpedanceCfg->HstiaRtiaSel = GetHSTIARtia(ConfigRtiaVal);
pImpedanceCfg->BiasVolt = 0.0;
pImpedanceCfg->SweepCfg.SweepEn = bFALSE;
pImpedanceCfg->SweepCfg.SweepStart = 100.0f;
pImpedanceCfg->SweepCfg.SweepStop = 100000.0f;
pImpedanceCfg->SweepCfg.SweepPoints = 50;
pImpedanceCfg->SweepCfg.SweepLog = bTRUE;
pImpedanceCfg->PwrMod = AFEPWR_LP;
pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_4;
pImpedanceCfg->DftNum = DFTNUM_16384;
pImpedanceCfg->DftSrc = DFTSRC_SINC3;
}
void AD5940AMPStructInit(void)
{
AppAMPCfg_Type *pAMPCfg;
AppAMPGetCfg(&pAMPCfg);
pAMPCfg->WuptClkFreq = LFOSCFreq;
pAMPCfg->SeqStartAddr = 0;
pAMPCfg->MaxSeqLen = 512;
pAMPCfg->RcalVal = 100.0;
pAMPCfg->NumOfData = -1;
pAMPCfg->AmpODR = 1.0;
pAMPCfg->FifoThresh = 4;
pAMPCfg->SensorBias = 0;
pAMPCfg->LptiaRtiaSel = GetLPTIARtia(ConfigRtiaVal);
pAMPCfg->LpTiaRl = LPTIARLOAD_10R;
pAMPCfg->LpTiaRf = CurrentLpTiaRf; // Use configured LPF
pAMPCfg->Vzero = 1100;
pAMPCfg->ADCRefVolt = 1.82;
}
void AD5940RampStructInit(void)
{
AppRAMPCfg_Type *pRampCfg;
AppRAMPGetCfg(&pRampCfg);
pRampCfg->SeqStartAddr = 0;
pRampCfg->MaxSeqLen = 1024;
pRampCfg->RcalVal = 100.0;
pRampCfg->ADCRefVolt = 1820.0f;
pRampCfg->FifoThresh = 4;
pRampCfg->SysClkFreq = 16000000.0f;
pRampCfg->LFOSCClkFreq = LFOSCFreq;
pRampCfg->RampStartVolt = -500.0f;
pRampCfg->RampPeakVolt = +500.0f;
pRampCfg->VzeroStart = 1100.0f;
pRampCfg->VzeroPeak = 1100.0f;
pRampCfg->StepNumber = 100;
pRampCfg->RampDuration = 10000;
pRampCfg->SampleDelay = 1.0f;
pRampCfg->LPTIARtiaSel = GetLPTIARtia(ConfigRtiaVal);
pRampCfg->LPTIARloadSel = LPTIARLOAD_10R;
pRampCfg->LpTiaRf = CurrentLpTiaRf; // Use configured LPF
pRampCfg->AdcPgaGain = ADCPGA_1P5;
}
static int32_t AD5940PlatformCfg(void)
{
CLKCfg_Type clk_cfg;
@ -121,7 +235,6 @@ static int32_t AD5940PlatformCfg(void)
AD5940_HWReset();
AD5940_Initialize();
// Use HFOSC (16MHz) for stability across all frequencies
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
clk_cfg.SysClkDiv = SYSCLKDIV_1;
@ -144,7 +257,7 @@ static int32_t AD5940PlatformCfg(void)
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH, bTRUE);
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH|AFEINTSRC_CUSTOMINT0, bTRUE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
gpio_cfg.FuncSet = GP0_INT;
@ -168,17 +281,28 @@ void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
{
float mag = pImp[i].Magnitude;
float phase = pImp[i].Phase;
float real = mag * cosf(phase);
float imag = mag * sinf(phase);
printf("DATA,%.2f,%.4f,%.4f,%.4f,%.4f\n",
freq,
mag,
phase * 180.0f / MATH_PI,
real,
imag
);
printf("DATA,%.2f,%.4f,%.4f,%.4f,%.4f\n", freq, mag, phase * 180.0f / MATH_PI, real, imag);
}
}
void AmperometricShowResult(float *pData, uint32_t DataCount)
{
static int index = 0;
for(int i=0;i<DataCount;i++)
{
printf("AMP,%d,%.4f\n", index++, pData[i]);
}
}
void RampShowResult(float *pData, uint32_t DataCount)
{
static int index = 0;
for(int i=0;i<DataCount;i++)
{
printf("RAMP,%d,%.4f\n", index++, pData[i]);
}
}
@ -188,22 +312,37 @@ void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
void Routine_CalibrateLFO(void) {
printf(">> Calibrating LFOSC...\n");
AppIMPCleanup();
AppIMPCalibrateLFO();
printf(">> LFOSC Calibrated.\n");
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
LFOSCMeasure_Type cal_cfg;
cal_cfg.CalDuration = 1000.0;
cal_cfg.CalSeqAddr = 0;
cal_cfg.SystemClkFreq = 16000000.0;
if(AD5940_LFOSCMeasure(&cal_cfg, &LFOSCFreq) == AD5940ERR_OK) {
printf(">> LFOSC Calibrated: %.2f Hz\n", LFOSCFreq);
} else {
printf(">> LFOSC Calibration Failed.\n");
}
}
void Routine_Measure(float freq) {
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IMPEDANCE;
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
AppIMPCleanup();
AppIMPCalibrateLFO();
pCfg->WuptClkFreq = LFOSCFreq;
pCfg->HstiaRtiaSel = GetHSTIARtia(ConfigRtiaVal);
pCfg->RtiaVal = RtiaCalibrationTable[CurrentRtiaIndex];
pCfg->SweepCfg.SweepEn = bFALSE;
pCfg->SinFreq = freq;
pCfg->NumOfData = -1;
pCfg->RealDataCount = -1; // Disable filtering
pCfg->RealDataCount = -1;
pCfg->bParaChanged = bTRUE;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
@ -214,23 +353,22 @@ void Routine_Measure(float freq) {
}
void Routine_Sweep(float start, float end, int steps) {
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IMPEDANCE;
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
AppIMPCleanup();
AppIMPCalibrateLFO();
pCfg->WuptClkFreq = LFOSCFreq;
pCfg->HstiaRtiaSel = GetHSTIARtia(ConfigRtiaVal);
pCfg->RtiaVal = RtiaCalibrationTable[CurrentRtiaIndex];
pCfg->SweepCfg.SweepEn = bTRUE;
pCfg->SweepCfg.SweepStart = start;
pCfg->SweepCfg.SweepStop = end;
// DUMMY POINT STRATEGY:
// Request steps + 1 from the engine, but tell the ISR to only report 'steps'.
// The artifact will happen on the +1 point, which is discarded.
pCfg->SweepCfg.SweepPoints = steps + 1;
pCfg->NumOfData = steps + 1;
pCfg->RealDataCount = steps; // Stop reporting after this count
pCfg->RealDataCount = steps;
pCfg->SweepCfg.SweepLog = bTRUE;
pCfg->bParaChanged = bTRUE;
@ -241,13 +379,69 @@ void Routine_Sweep(float start, float end, int steps) {
}
}
void Routine_Amperometric(float bias_mv) {
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_AMPEROMETRIC;
printf(">> Starting Amperometry (Bias: %.1f mV, Range: %d)...\n", bias_mv, ConfigRtiaVal);
AppAMPCfg_Type *pCfg;
AppAMPGetCfg(&pCfg);
AD5940AMPStructInit();
pCfg->SensorBias = bias_mv;
pCfg->WuptClkFreq = LFOSCFreq;
pCfg->LptiaRtiaSel = GetLPTIARtia(ConfigRtiaVal);
pCfg->ReDoRtiaCal = bTRUE;
if(AppAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppAMPCtrl(AMPCTRL_START, 0);
} else {
printf("ERROR: AMP Init Failed\n");
}
}
void Routine_LSV(float start_mv, float end_mv, int steps, int duration_ms) {
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_RAMP;
printf(">> Starting LSV (%.1f to %.1f mV, %d steps, %d ms)...\n", start_mv, end_mv, steps, duration_ms);
AppRAMPCfg_Type *pCfg;
AppRAMPGetCfg(&pCfg);
AD5940RampStructInit();
pCfg->RampStartVolt = start_mv;
pCfg->RampPeakVolt = end_mv;
pCfg->StepNumber = steps;
pCfg->RampDuration = duration_ms;
pCfg->LFOSCClkFreq = LFOSCFreq;
pCfg->LPTIARtiaSel = GetLPTIARtia(ConfigRtiaVal);
// Use global calibration value
pCfg->RtiaValue.Magnitude = RtiaCalibrationTable[CurrentRtiaIndex];
pCfg->RtiaValue.Phase = 0;
pCfg->bRampOneDir = bTRUE; // Linear Sweep (not Cyclic)
pCfg->bParaChanged = bTRUE;
if(AppRAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppRAMPCtrl(APPCTRL_START, 0);
} else {
printf("ERROR: RAMP Init Failed\n");
}
}
void Routine_CalibrateSystem(void) {
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IMPEDANCE;
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
AppIMPCleanup();
// 1. ADC Calibration
ADCPGACal_Type adcpga_cal;
adcpga_cal.AdcClkFreq = 16000000.0;
adcpga_cal.SysClkFreq = 16000000.0;
@ -261,18 +455,15 @@ void Routine_CalibrateSystem(void) {
printf(">> Calibrating ADC Offset...\n");
AD5940_ADCPGACal(&adcpga_cal);
// 2. HSDAC Configuration
HSDACCfg_Type hsdac_cfg;
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_0P25;
hsdac_cfg.HsDacGain = HSDACGAIN_0P2;
hsdac_cfg.HsDacUpdateRate = 7;
AD5940_HSDacCfgS(&hsdac_cfg);
// 3. RTIA Calibration
HSRTIACal_Type hsrtia_cal;
fImpPol_Type Res;
memset(&hsrtia_cal, 0, sizeof(hsrtia_cal));
hsrtia_cal.fFreq = 1000.0f;
hsrtia_cal.AdcClkFreq = 16000000.0;
hsrtia_cal.SysClkFreq = 16000000.0;
@ -283,15 +474,16 @@ void Routine_CalibrateSystem(void) {
hsrtia_cal.HsTiaCfg.DiodeClose = bFALSE;
hsrtia_cal.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
hsrtia_cal.HsTiaCfg.HstiaCtia = 31;
hsrtia_cal.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_200;
hsrtia_cal.HsTiaCfg.HstiaRtiaSel = GetHSTIARtia(ConfigRtiaVal);
hsrtia_cal.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
hsrtia_cal.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
hsrtia_cal.DftCfg.DftNum = DFTNUM_16384;
hsrtia_cal.DftCfg.DftSrc = DFTSRC_SINC3;
printf(">> Calibrating HSTIARTIA_200...\n");
printf(">> Calibrating RTIA %d Ohm...\n", ConfigRtiaVal);
if (AD5940_HSRtiaCal(&hsrtia_cal, &Res) == AD5940ERR_OK) {
printf("Calibrated Rtia: Mag = %f Ohm, Phase = %f\n", Res.Magnitude, Res.Phase);
RtiaCalibrationTable[CurrentRtiaIndex] = Res.Magnitude;
pCfg->RtiaVal = Res.Magnitude;
} else {
printf("Calibration Failed\n");
@ -307,14 +499,38 @@ int input_pos = 0;
void process_command() {
char cmd = input_buffer[0];
sleep_ms(10);
if (cmd == 'v') {
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID);
printf("CHIP_ID:0x%04X\n", id);
}
else if (cmd == 'r') {
if (strlen(input_buffer) > 2) {
uint32_t val = atoi(input_buffer + 2);
ConfigRtiaVal = val;
CurrentRtiaIndex = GetRtiaIndex(val);
printf("RANGE_SET:%d\n", ConfigRtiaVal);
}
}
else if (cmd == 'f') {
if (strlen(input_buffer) > 2) {
int idx = atoi(input_buffer + 2);
switch(idx) {
case 0: CurrentLpTiaRf = LPTIARF_BYPASS; break;
case 1: CurrentLpTiaRf = LPTIARF_20K; break;
case 2: CurrentLpTiaRf = LPTIARF_100K; break;
case 3: CurrentLpTiaRf = LPTIARF_200K; break;
case 4: CurrentLpTiaRf = LPTIARF_400K; break;
case 5: CurrentLpTiaRf = LPTIARF_600K; break;
case 6: CurrentLpTiaRf = LPTIARF_1M; break;
default: CurrentLpTiaRf = LPTIARF_20K; break;
}
printf("LPF_SET:%d\n", idx);
}
}
else if (cmd == 'c') {
Routine_CalibrateLFO();
Routine_CalibrateSystem();
}
else if (cmd == 'm') {
@ -328,8 +544,23 @@ void process_command() {
if (strlen(input_buffer) > 2) sscanf(input_buffer + 2, "%f %f %d", &start, &end, &steps);
Routine_Sweep(start, end, steps);
}
else if (cmd == 'a') {
float bias = 0.0f;
if (strlen(input_buffer) > 2) bias = atof(input_buffer + 2);
Routine_Amperometric(bias);
}
else if (cmd == 'l') {
// l <start> <end> <steps> <duration>
float start = -500.0f, end = 500.0f;
int steps = 100, duration = 10000;
if (strlen(input_buffer) > 2) sscanf(input_buffer + 2, "%f %f %d %d", &start, &end, &steps, &duration);
Routine_LSV(start, end, steps, duration);
}
else if (cmd == 'x') {
AppIMPCleanup();
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IDLE;
printf("STOPPED\n");
}
else if (cmd == 'z') {
@ -344,6 +575,10 @@ int main() {
setup_pins();
AD5940PlatformCfg();
AD5940ImpedanceStructInit();
AD5940AMPStructInit();
AD5940RampStructInit();
Routine_CalibrateLFO();
printf("SYSTEM_READY\n");
@ -361,15 +596,47 @@ int main() {
if (gpio_get(PIN_INT) == 0) {
uint32_t temp = APPBUFF_SIZE;
int32_t status = AppIMPISR(AppBuff, &temp);
int32_t status = 0;
if (CurrentMode == MODE_IMPEDANCE) {
status = AppIMPISR(AppBuff, &temp);
if (status == AD5940ERR_FIFO) {
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
AppIMPCleanup();
CurrentMode = MODE_IDLE;
} else if(temp > 0) {
ImpedanceShowResult(AppBuff, temp);
}
}
else if (CurrentMode == MODE_AMPEROMETRIC) {
status = AppAMPISR(AppBuff, &temp);
if (status == AD5940ERR_FIFO) {
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IDLE;
} else if(temp > 0) {
AmperometricShowResult((float*)AppBuff, temp);
}
}
else if (CurrentMode == MODE_RAMP) {
status = AppRAMPISR(AppBuff, &temp);
if (status == AD5940ERR_FIFO) {
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IDLE;
} else if(temp > 0) {
RampShowResult((float*)AppBuff, temp);
}
}
if (status == AD5940ERR_STOP) {
printf("STOPPED\n");
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
CurrentMode = MODE_IDLE;
}
}
}
return 0;
}