i know this shit is SLOPPY rn but we finally have DATA even if it is in the form of a COMPLETE MALFUNTION wooo

This commit is contained in:
pszsh 2026-01-23 03:54:51 -08:00
parent 6e1d6cc970
commit b96e59e543
9 changed files with 900 additions and 1767 deletions

View File

@ -14,6 +14,7 @@ pico_sdk_init()
add_executable(EIS
main.c
ad5940.c
Impedance.c
)
target_compile_definitions(EIS PRIVATE CHIPSEL_594X)

669
Impedance.c Normal file
View File

@ -0,0 +1,669 @@
// File: Impedance.c
#include "ad5940.h"
#include <stdio.h>
#include "string.h"
#include "math.h"
#include "Impedance.h"
/* Default LPDAC resolution(2.5V internal reference). */
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
AppIMPCfg_Type AppIMPCfg =
{
.bParaChanged = bFALSE,
.SeqStartAddr = 0,
.MaxSeqLen = 0,
.SeqStartAddrCal = 0,
.MaxSeqLenCal = 0,
.ImpODR = 20.0, /* 20.0 Hz = 50ms period */
.NumOfData = -1,
.SysClkFreq = 16000000.0,
.WuptClkFreq = 32000.0,
.AdcClkFreq = 16000000.0,
.RcalVal = 10000.0,
.RtiaVal = 200.0,
.DswitchSel = SWD_CE0,
.PswitchSel = SWP_CE0,
.NswitchSel = SWN_SE0,
.TswitchSel = SWT_TRTIA,
.PwrMod = AFEPWR_HP,
.HstiaRtiaSel = HSTIARTIA_200,
.ExtRtia = 0,
.ExcitBufGain = EXCITBUFGAIN_0P25,
.HsDacGain = HSDACGAIN_0P2,
.HsDacUpdateRate = 7,
.DacVoltPP = 600.0,
.BiasVolt = -0.0f,
.SinFreq = 1000.0,
.DftNum = DFTNUM_16384,
.DftSrc = DFTSRC_SINC3,
.HanWinEn = bTRUE,
.AdcPgaGain = ADCPGA_1P5,
.ADCSinc3Osr = ADCSINC3OSR_2,
.ADCSinc2Osr = ADCSINC2OSR_22,
.ADCAvgNum = ADCAVGNUM_16,
.SweepCfg.SweepEn = bTRUE,
.SweepCfg.SweepStart = 1000,
.SweepCfg.SweepStop = 100000.0,
.SweepCfg.SweepPoints = 101,
.SweepCfg.SweepLog = bFALSE,
.SweepCfg.SweepIndex = 0,
.FifoThresh = 6, /* 3 measurements * 2 (Real/Imag) = 6 words */
.IMPInited = bFALSE,
.StopRequired = bFALSE,
};
int32_t AppIMPGetCfg(void *pCfg)
{
if(pCfg)
{
*(AppIMPCfg_Type**)pCfg = &AppIMPCfg;
return AD5940ERR_OK;
}
return AD5940ERR_PARA;
}
int32_t AppIMPCtrl(uint32_t Command, void *pPara)
{
switch (Command)
{
case IMPCTRL_START:
{
WUPTCfg_Type wupt_cfg;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
if(AppIMPCfg.IMPInited == bFALSE) return AD5940ERR_APPERROR;
wupt_cfg.WuptEn = bTRUE;
wupt_cfg.WuptEndSeq = WUPTENDSEQ_A;
wupt_cfg.WuptOrder[0] = SEQID_0;
wupt_cfg.SeqxSleepTime[SEQID_0] = 4;
// Calculate Wakeup Time based on ODR
// 32kHz clock. For 20Hz: 32000/20 = 1600 ticks.
uint32_t wait_cycles = (uint32_t)(AppIMPCfg.WuptClkFreq/AppIMPCfg.ImpODR);
if(wait_cycles < 20) wait_cycles = 20; // Minimum safety guard
wupt_cfg.SeqxWakeupTime[SEQID_0] = wait_cycles - 4;
AD5940_WUPTCfg(&wupt_cfg);
AppIMPCfg.FifoDataCount = 0;
break;
}
case IMPCTRL_STOPNOW:
{
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
AD5940_WUPTCtrl(bFALSE);
AD5940_WUPTCtrl(bFALSE);
AD5940_SEQCtrlS(bFALSE);
break;
}
case IMPCTRL_STOPSYNC:
{
AppIMPCfg.StopRequired = bTRUE;
break;
}
case IMPCTRL_GETFREQ:
{
if(pPara == 0) return AD5940ERR_PARA;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
*(float*)pPara = AppIMPCfg.FreqofData;
else
*(float*)pPara = AppIMPCfg.SinFreq;
break;
}
case IMPCTRL_SHUTDOWN:
{
AppIMPCtrl(IMPCTRL_STOPNOW, 0);
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();
break;
}
default:
break;
}
return AD5940ERR_OK;
}
float AppIMPGetCurrFreq(void)
{
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
return AppIMPCfg.FreqofData;
else
return AppIMPCfg.SinFreq;
}
static AD5940Err AppIMPSeqCfgGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
HSLoopCfg_Type HsLoopCfg;
DSPCfg_Type dsp_cfg;
float sin_freq;
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 = bFALSE;
aferef_cfg.Lp1V8BuffEn = bFALSE;
aferef_cfg.LpBandgapEn = bTRUE;
aferef_cfg.LpRefBufEn = bTRUE;
aferef_cfg.LpRefBoostEn = bFALSE;
AD5940_REFCfgS(&aferef_cfg);
HsLoopCfg.HsDacCfg.ExcitBufGain = AppIMPCfg.ExcitBufGain;
HsLoopCfg.HsDacCfg.HsDacGain = AppIMPCfg.HsDacGain;
HsLoopCfg.HsDacCfg.HsDacUpdateRate = AppIMPCfg.HsDacUpdateRate;
HsLoopCfg.HsTiaCfg.DiodeClose = bFALSE;
HsLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
HsLoopCfg.HsTiaCfg.HstiaCtia = 31;
HsLoopCfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
HsLoopCfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
HsLoopCfg.HsTiaCfg.HstiaRtiaSel = AppIMPCfg.HstiaRtiaSel;
HsLoopCfg.HsTiaCfg.ExtRtia = AppIMPCfg.ExtRtia;
HsLoopCfg.SWMatCfg.Dswitch = AppIMPCfg.DswitchSel;
HsLoopCfg.SWMatCfg.Pswitch = AppIMPCfg.PswitchSel;
HsLoopCfg.SWMatCfg.Nswitch = AppIMPCfg.NswitchSel;
HsLoopCfg.SWMatCfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
HsLoopCfg.WgCfg.WgType = WGTYPE_SIN;
HsLoopCfg.WgCfg.GainCalEn = bTRUE;
HsLoopCfg.WgCfg.OffsetCalEn = bTRUE;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCfg.SweepStart;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepCfg.SweepStart;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
sin_freq = AppIMPCfg.SweepCurrFreq;
}
else
{
sin_freq = AppIMPCfg.SinFreq;
AppIMPCfg.FreqofData = sin_freq;
}
HsLoopCfg.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(sin_freq, AppIMPCfg.SysClkFreq);
HsLoopCfg.WgCfg.SinCfg.SinAmplitudeWord = (uint32_t)(AppIMPCfg.DacVoltPP/800.0f*2047 + 0.5f);
HsLoopCfg.WgCfg.SinCfg.SinOffsetWord = 0;
HsLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
AD5940_HSLoopCfgS(&HsLoopCfg);
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
dsp_cfg.ADCBaseCfg.ADCPga = AppIMPCfg.AdcPgaGain;
memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
dsp_cfg.ADCFilterCfg.ADCAvgNum = AppIMPCfg.ADCAvgNum;
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ;
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
dsp_cfg.DftCfg.DftNum = AppIMPCfg.DftNum;
dsp_cfg.DftCfg.DftSrc = AppIMPCfg.DftSrc;
dsp_cfg.DftCfg.HanWinEn = AppIMPCfg.HanWinEn;
memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg));
AD5940_DSPCfgS(&dsp_cfg);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
AD5940_SEQGenInsert(SEQ_STOP());
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE);
if(error == AD5940ERR_OK)
{
AppIMPCfg.InitSeqInfo.SeqId = SEQID_1;
AppIMPCfg.InitSeqInfo.SeqRamAddr = AppIMPCfg.SeqStartAddr;
AppIMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.InitSeqInfo.SeqLen = SeqLen;
AD5940_SEQCmdWrite(AppIMPCfg.InitSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error;
return AD5940ERR_OK;
}
static AD5940Err AppIMPSeqMeasureGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
uint32_t WaitClks;
SWMatrixCfg_Type sw_cfg;
ClksCalInfo_Type clks_cal;
clks_cal.DataType = DATATYPE_DFT;
clks_cal.DftSrc = AppIMPCfg.DftSrc;
clks_cal.DataCount = 1L<<(AppIMPCfg.DftNum+2);
clks_cal.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
clks_cal.ADCAvgNum = AppIMPCfg.ADCAvgNum;
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
AD5940_SEQGenCtrl(bTRUE);
AD5940_SEQGpioCtrlS(AGPIO_Pin2);
AD5940_SEQGenInsert(SEQ_WAIT(16*250));
/* ----------------------------------------------------------------------- */
/* Measurement 1: RCAL (Current) */
/* ----------------------------------------------------------------------- */
sw_cfg.Dswitch = SWD_RCAL0;
sw_cfg.Pswitch = SWP_RCAL0;
sw_cfg.Nswitch = SWN_RCAL1;
sw_cfg.Tswitch = SWT_RCAL1|SWT_TRTIA;
AD5940_SWMatrixCfgS(&sw_cfg);
/* ADC Mux for Current (HSTIA) */
AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); // Settling
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[0]);
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bFALSE);
/* ----------------------------------------------------------------------- */
/* Measurement 2: Sensor Current (I) */
/* ----------------------------------------------------------------------- */
sw_cfg.Dswitch = AppIMPCfg.DswitchSel;
sw_cfg.Pswitch = AppIMPCfg.PswitchSel;
sw_cfg.Nswitch = AppIMPCfg.NswitchSel;
sw_cfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
AD5940_SWMatrixCfgS(&sw_cfg);
/* ADC Mux for Current (HSTIA) */
AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[1]);
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bFALSE);
/* ----------------------------------------------------------------------- */
/* Measurement 3: Sensor Voltage (V) */
/* ----------------------------------------------------------------------- */
/* Switches remain same (Force path active) */
/* ADC Mux for Voltage (AIN2/AIN3) */
AD5940_ADCMuxCfgS(ADCMUXP_AIN2, ADCMUXN_AIN3);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[2]);
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bFALSE);
AD5940_SEQGpioCtrlS(0);
AD5940_EnterSleepS();
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE);
if(error == AD5940ERR_OK)
{
AppIMPCfg.MeasureSeqInfo.SeqId = SEQID_0;
AppIMPCfg.MeasureSeqInfo.SeqRamAddr = AppIMPCfg.InitSeqInfo.SeqRamAddr + AppIMPCfg.InitSeqInfo.SeqLen ;
AppIMPCfg.MeasureSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.MeasureSeqInfo.SeqLen = SeqLen;
AD5940_SEQCmdWrite(AppIMPCfg.MeasureSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error;
return AD5940ERR_OK;
}
AD5940Err AppIMPCheckFreq(float freq)
{
ADCFilterCfg_Type filter_cfg;
DFTCfg_Type dft_cfg;
HSDACCfg_Type hsdac_cfg;
uint32_t WaitClks;
ClksCalInfo_Type clks_cal;
FreqParams_Type freq_params;
uint32_t SeqCmdBuff[32];
uint32_t SRAMAddr = 0;
freq_params = AD5940_GetFreqParameters(freq);
if(freq < 0.51)
{
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2;
hsdac_cfg.HsDacGain = HSDACGAIN_1;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_40K);
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
AD5940_HPModeEn(bFALSE);
}
else if(freq < 5 )
{
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2;
hsdac_cfg.HsDacGain = HSDACGAIN_1;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_40K);
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
AD5940_HPModeEn(bFALSE);
}
else if(freq < 450)
{
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.ADCSinc2Osr = freq_params.ADCSinc2Osr;
filter_cfg.ADCSinc3Osr = freq_params.ADCSinc3Osr;
filter_cfg.BpSinc3 = bFALSE;
filter_cfg.BpNotch = bTRUE;
filter_cfg.Sinc2NotchEnable = bTRUE;
dft_cfg.DftNum = freq_params.DftNum;
dft_cfg.DftSrc = freq_params.DftSrc;
dft_cfg.HanWinEn = AppIMPCfg.HanWinEn;
AD5940_ADCFilterCfgS(&filter_cfg);
AD5940_DFTCfgS(&dft_cfg);
clks_cal.DataType = DATATYPE_DFT;
clks_cal.DftSrc = freq_params.DftSrc;
clks_cal.DataCount = 1L<<(freq_params.DftNum+2);
clks_cal.ADCSinc2Osr = freq_params.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = freq_params.ADCSinc3Osr;
clks_cal.ADCAvgNum = 0;
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
// Update Wait Times for all 3 measurements
for (int i = 0; i < 3; i++) {
SRAMAddr = AppIMPCfg.MeasureSeqInfo.SeqRamAddr + AppIMPCfg.SeqWaitAddr[i];
SeqCmdBuff[0] = SEQ_WAIT(WaitClks/2);
SeqCmdBuff[1] = SEQ_WAIT(WaitClks/2);
AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 2);
}
return AD5940ERR_OK;
}
int32_t AppIMPInit(uint32_t *pBuffer, uint32_t BufferSize)
{
AD5940Err error = AD5940ERR_OK;
SEQCfg_Type seq_cfg;
FIFOCfg_Type fifo_cfg;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
// CRITICAL: Stop WUPT and Sequencer before reconfiguration
AD5940_WUPTCtrl(bFALSE);
AD5940_SEQCtrlS(bFALSE);
seq_cfg.SeqMemSize = SEQMEMSIZE_2KB;
seq_cfg.SeqBreakEn = bFALSE;
seq_cfg.SeqIgnoreEn = bTRUE;
seq_cfg.SeqCntCRCClr = bTRUE;
seq_cfg.SeqEnable = bFALSE;
seq_cfg.SeqWrTimer = 0;
AD5940_SEQCfg(&seq_cfg);
AD5940_FIFOCtrlS(FIFOSRC_DFT, bFALSE);
fifo_cfg.FIFOEn = bTRUE;
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
fifo_cfg.FIFOThresh = AppIMPCfg.FifoThresh;
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
if((AppIMPCfg.IMPInited == bFALSE)||(AppIMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
AD5940_SEQGenInit(pBuffer, BufferSize);
error = AppIMPSeqCfgGen();
if(error != AD5940ERR_OK) return error;
error = AppIMPSeqMeasureGen();
if(error != AD5940ERR_OK) return error;
AppIMPCfg.bParaChanged = bFALSE;
}
AppIMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.InitSeqInfo);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg);
AD5940_SEQMmrTrig(AppIMPCfg.InitSeqInfo.SeqId);
// Safety timeout for Init Sequence
int timeout = 100000;
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE) {
if(--timeout <= 0) {
return AD5940ERR_TIMEOUT; // Return error if init fails
}
}
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
AppIMPCfg.MeasureSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.MeasureSeqInfo);
AppIMPCheckFreq(AppIMPCfg.FreqofData);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg);
AD5940_ClrMCUIntFlag();
AppIMPCfg.IMPInited = bTRUE;
return AD5940ERR_OK;
}
int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
{
if(AppIMPCfg.NumOfData > 0)
{
AppIMPCfg.FifoDataCount += *pDataCount/6; // 6 words per point
if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
}
if(AppIMPCfg.StopRequired == bTRUE)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
if(AppIMPCfg.SweepCfg.SweepEn)
{
AD5940_WGFreqCtrlS(AppIMPCfg.SweepNextFreq, AppIMPCfg.SysClkFreq);
AppIMPCheckFreq(AppIMPCfg.SweepNextFreq);
}
return AD5940ERR_OK;
}
int32_t AppIMPDataProcess(int32_t * const pData, uint32_t *pDataCount)
{
uint32_t DataCount = *pDataCount;
uint32_t ImpResCount = DataCount/6; // 3 measurements * 2 words
fImpPol_Type * const pOut = (fImpPol_Type*)pData;
iImpCar_Type * pSrcData = (iImpCar_Type*)pData;
*pDataCount = 0;
for(uint32_t i=0; i<DataCount; i++)
{
pData[i] &= 0x3ffff;
if(pData[i]&(1L<<17))
{
pData[i] |= 0xfffc0000;
}
}
for(uint32_t i=0; i<ImpResCount; i++)
{
iImpCar_Type *pDftRcal, *pDftI, *pDftV;
pDftRcal = pSrcData++;
pDftI = pSrcData++;
pDftV = pSrcData++;
float MagI, PhaseI;
float MagV, PhaseV;
float ZMag, ZPhase;
// Calculate Magnitude and Phase for Current (I)
MagI = sqrt((float)pDftI->Real*pDftI->Real + (float)pDftI->Image*pDftI->Image);
PhaseI = atan2(-pDftI->Image, pDftI->Real);
// Calculate Magnitude and Phase for Voltage (V)
MagV = sqrt((float)pDftV->Real*pDftV->Real + (float)pDftV->Image*pDftV->Image);
PhaseV = atan2(-pDftV->Image, pDftV->Real);
// Z = V / I * Rtia
if(MagI > 1e-9) // Avoid division by zero
ZMag = (MagV / MagI) * AppIMPCfg.RtiaVal;
else
ZMag = 0;
ZPhase = PhaseV - PhaseI;
// Normalize Phase to -PI to +PI
while(ZPhase > MATH_PI) ZPhase -= 2*MATH_PI;
while(ZPhase < -MATH_PI) ZPhase += 2*MATH_PI;
pOut[i].Magnitude = ZMag;
pOut[i].Phase = ZPhase;
}
*pDataCount = ImpResCount;
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepNextFreq;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
}
return 0;
}
int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
{
uint32_t BuffCount;
uint32_t FifoCnt;
BuffCount = *pCount;
*pCount = 0;
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
AD5940_SleepKeyCtrlS(SLPKEY_LOCK);
if(AD5940_INTCTestFlag(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH) == bTRUE)
{
FifoCnt = (AD5940_FIFOGetCnt()/6)*6; // Align to 6 words
if (FifoCnt == 0) {
// Safety: If interrupt fired but no data, clear flag and exit
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
return 0;
}
if(FifoCnt > BuffCount)
{
FifoCnt = BuffCount;
}
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AppIMPRegModify(pBuff, &FifoCnt);
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
AppIMPDataProcess((int32_t*)pBuff, &FifoCnt);
*pCount = FifoCnt;
return 0;
}
return 0;
}

View File

@ -1,17 +1,4 @@
/*!
*****************************************************************************
@file: Impedance.h
@author: Neo XU
@brief: 4-wire/2-wire impedance measurement header file.
-----------------------------------------------------------------------------
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.
*****************************************************************************/
// File: Impedance.h
#ifndef _IMPEDANCESEQUENCES_H_
#define _IMPEDANCESEQUENCES_H_
#include "ad5940.h"
@ -26,7 +13,7 @@ typedef struct
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 SeqWaitAddr[2];
uint32_t SeqWaitAddr[3]; /* Addresses of the Wait commands in the sequence, used to update delays for frequency changes */
uint32_t MaxSeqLenCal;
/* Application related parameters */
float ImpODR; /* */
@ -35,6 +22,7 @@ typedef struct
float SysClkFreq; /* The real frequency of system clock */
float AdcClkFreq; /* The real frequency of ADC clock */
float RcalVal; /* Rcal value in Ohm */
float RtiaVal; /* Rtia value in Ohm (used for 4-wire calculation) */
/* Switch Configuration */
uint32_t DswitchSel;
uint32_t PswitchSel;
@ -42,6 +30,7 @@ typedef struct
uint32_t TswitchSel;
uint32_t PwrMod; /* Control Chip power mode(LP/HP) */
uint32_t HstiaRtiaSel; /* 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 ExtRtia; /* External RTIA switch control/value */
uint32_t ExcitBufGain; /* Select from EXCTBUFGAIN_2, EXCTBUFGAIN_0P25 */
uint32_t HsDacGain; /* Select from HSDACGAIN_1, HSDACGAIN_0P2 */
uint32_t HsDacUpdateRate;
@ -58,7 +47,6 @@ typedef struct
/* Sweep Function Control */
SoftSweepCfg_Type SweepCfg;
uint32_t FifoThresh; /* FIFO threshold. Should be N*4 */
/* Private variables for internal usage */
/* Private variables for internal usage */
float SweepCurrFreq;
float SweepNextFreq;
@ -76,10 +64,9 @@ typedef struct
#define IMPCTRL_GETFREQ 3 /* Get Current frequency of returned data from ISR */
#define IMPCTRL_SHUTDOWN 4 /* Note: shutdown here means turn off everything and put AFE to hibernate mode. The word 'SHUT DOWN' is only used here. */
int32_t AppIMPInit(uint32_t *pBuffer, uint32_t BufferSize);
int32_t AppIMPGetCfg(void *pCfg);
int32_t AppIMPISR(void *pBuff, uint32_t *pCount);
int32_t AppIMPCtrl(uint32_t Command, void *pPara);
#endif
#endif

View File

@ -1,678 +0,0 @@
/*!
*****************************************************************************
@file: Impedance.c
@author: Neo Xu
@brief: Electrochemical impedance spectroscopy based on example AD5940_Impedance
-----------------------------------------------------------------------------
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 "AD5940.H"
#include <stdio.h>
#include "string.h"
#include "math.h"
#include "Impedance.h"
/* Default LPDAC resolution(2.5V internal reference). */
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
/*
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
*/
AppIMPCfg_Type AppIMPCfg =
{
.bParaChanged = bFALSE,
.SeqStartAddr = 0,
.MaxSeqLen = 0,
.SeqStartAddrCal = 0,
.MaxSeqLenCal = 0,
.ImpODR = 20.0, /* 20.0 Hz*/
.NumOfData = -1,
.SysClkFreq = 16000000.0,
.WuptClkFreq = 32000.0,
.AdcClkFreq = 16000000.0,
.RcalVal = 10000.0,
.DswitchSel = SWD_CE0,
.PswitchSel = SWP_CE0,
.NswitchSel = SWN_AIN1,
.TswitchSel = SWT_AIN1,
.PwrMod = AFEPWR_LP,
.LptiaRtiaSel = LPTIARTIA_4K, /* COnfigure RTIA */
.LpTiaRf = LPTIARF_1M, /* Configure LPF resistor */
.LpTiaRl = LPTIARLOAD_100R,
.HstiaRtiaSel = HSTIARTIA_1K,
.ExcitBufGain = EXCITBUFGAIN_0P25,
.HsDacGain = HSDACGAIN_0P2,
.HsDacUpdateRate = 0x1B,
.DacVoltPP = 300.0,
.BiasVolt = -0.0f,
.SinFreq = 100000.0, /* 1000Hz */
.DftNum = DFTNUM_16384,
.DftSrc = DFTSRC_SINC3,
.HanWinEn = bTRUE,
.AdcPgaGain = ADCPGA_1,
.ADCSinc3Osr = ADCSINC3OSR_2,
.ADCSinc2Osr = ADCSINC2OSR_22,
.ADCAvgNum = ADCAVGNUM_16,
.SweepCfg.SweepEn = bTRUE,
.SweepCfg.SweepStart = 1000,
.SweepCfg.SweepStop = 100000.0,
.SweepCfg.SweepPoints = 101,
.SweepCfg.SweepLog = bFALSE,
.SweepCfg.SweepIndex = 0,
.FifoThresh = 4,
.IMPInited = bFALSE,
.StopRequired = bFALSE,
};
/**
This function is provided for upper controllers that want to change
application parameters specially for user defined parameters.
*/
int32_t AppIMPGetCfg(void *pCfg)
{
if(pCfg)
{
*(AppIMPCfg_Type**)pCfg = &AppIMPCfg;
return AD5940ERR_OK;
}
return AD5940ERR_PARA;
}
int32_t AppIMPCtrl(uint32_t Command, void *pPara)
{
switch (Command)
{
case IMPCTRL_START:
{
WUPTCfg_Type wupt_cfg;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
if(AppIMPCfg.IMPInited == 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;
wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppIMPCfg.WuptClkFreq/AppIMPCfg.ImpODR)-4;
AD5940_WUPTCfg(&wupt_cfg);
AppIMPCfg.FifoDataCount = 0; /* restart */
break;
}
case IMPCTRL_STOPNOW:
{
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
/* Start Wupt right now */
AD5940_WUPTCtrl(bFALSE);
AD5940_WUPTCtrl(bFALSE);
break;
}
case IMPCTRL_STOPSYNC:
{
AppIMPCfg.StopRequired = bTRUE;
break;
}
case IMPCTRL_GETFREQ:
{
if(pPara == 0)
return AD5940ERR_PARA;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
*(float*)pPara = AppIMPCfg.FreqofData;
else
*(float*)pPara = AppIMPCfg.SinFreq;
}
break;
case IMPCTRL_SHUTDOWN:
{
AppIMPCtrl(IMPCTRL_STOPNOW, 0); /* Stop the measurement if it's running. */
/* Turn off LPloop related blocks which are not controlled automatically by hibernate operation */
AFERefCfg_Type aferef_cfg;
LPLoopCfg_Type lploop_cfg;
memset(&aferef_cfg, 0, sizeof(aferef_cfg));
AD5940_REFCfgS(&aferef_cfg);
memset(&lploop_cfg, 0, sizeof(lploop_cfg));
AD5940_LPLoopCfgS(&lploop_cfg);
AD5940_EnterSleepS(); /* Enter Hibernate */
}
break;
default:
break;
}
return AD5940ERR_OK;
}
/* generated code snnipet */
float AppIMPGetCurrFreq(void)
{
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
return AppIMPCfg.FreqofData;
else
return AppIMPCfg.SinFreq;
}
/* Application initialization */
static AD5940Err AppIMPSeqCfgGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
HSLoopCfg_Type HsLoopCfg;
LPLoopCfg_Type lploop_cfg;
DSPCfg_Type dsp_cfg;
float sin_freq;
/* 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 = bFALSE;
aferef_cfg.Lp1V8BuffEn = bFALSE;
aferef_cfg.LpBandgapEn = bTRUE;
aferef_cfg.LpRefBufEn = bTRUE;
aferef_cfg.LpRefBoostEn = bFALSE;
AD5940_REFCfgS(&aferef_cfg);
lploop_cfg.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
lploop_cfg.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VBIAS2PIN|LPDACSW_VZERO2LPTIA|LPDACSW_VZERO2PIN;
lploop_cfg.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
lploop_cfg.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
lploop_cfg.LpDacCfg.LpDacRef = LPDACREF_2P5;
lploop_cfg.LpDacCfg.DataRst = bFALSE;
lploop_cfg.LpDacCfg.PowerEn = bTRUE;
lploop_cfg.LpDacCfg.DacData6Bit = (uint32_t)((AppIMPCfg.Vzero-200)/DAC6BITVOLT_1LSB);
lploop_cfg.LpDacCfg.DacData12Bit =(int32_t)((AppIMPCfg.BiasVolt)/DAC12BITVOLT_1LSB) + lploop_cfg.LpDacCfg.DacData6Bit*64;
if(lploop_cfg.LpDacCfg.DacData12Bit>lploop_cfg.LpDacCfg.DacData6Bit*64)
lploop_cfg.LpDacCfg.DacData12Bit--;
lploop_cfg.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
lploop_cfg.LpAmpCfg.LpPaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaRf = AppIMPCfg.LpTiaRf;
lploop_cfg.LpAmpCfg.LpTiaRload = AppIMPCfg.LpTiaRl;
lploop_cfg.LpAmpCfg.LpTiaRtia = AppIMPCfg.LptiaRtiaSel;
lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
AD5940_LPLoopCfgS(&lploop_cfg);
HsLoopCfg.HsDacCfg.ExcitBufGain = AppIMPCfg.ExcitBufGain;
HsLoopCfg.HsDacCfg.HsDacGain = AppIMPCfg.HsDacGain;
HsLoopCfg.HsDacCfg.HsDacUpdateRate = AppIMPCfg.HsDacUpdateRate;
HsLoopCfg.HsTiaCfg.DiodeClose = bFALSE;
HsLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
HsLoopCfg.HsTiaCfg.HstiaCtia = 31; /* 31pF + 2pF */
HsLoopCfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
HsLoopCfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
HsLoopCfg.HsTiaCfg.HstiaRtiaSel = AppIMPCfg.HstiaRtiaSel;
HsLoopCfg.SWMatCfg.Dswitch = AppIMPCfg.DswitchSel;
HsLoopCfg.SWMatCfg.Pswitch = AppIMPCfg.PswitchSel;
HsLoopCfg.SWMatCfg.Nswitch = AppIMPCfg.NswitchSel;
HsLoopCfg.SWMatCfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
HsLoopCfg.WgCfg.WgType = WGTYPE_SIN;
HsLoopCfg.WgCfg.GainCalEn = bTRUE;
HsLoopCfg.WgCfg.OffsetCalEn = bTRUE;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCfg.SweepStart;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepCfg.SweepStart;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
sin_freq = AppIMPCfg.SweepCurrFreq;
}
else
{
sin_freq = AppIMPCfg.SinFreq;
AppIMPCfg.FreqofData = sin_freq;
}
HsLoopCfg.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(sin_freq, AppIMPCfg.SysClkFreq);
HsLoopCfg.WgCfg.SinCfg.SinAmplitudeWord = (uint32_t)(AppIMPCfg.DacVoltPP/800.0f*2047 + 0.5f);
HsLoopCfg.WgCfg.SinCfg.SinOffsetWord = 0;
HsLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
AD5940_HSLoopCfgS(&HsLoopCfg);
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
dsp_cfg.ADCBaseCfg.ADCPga = AppIMPCfg.AdcPgaGain;
memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
dsp_cfg.ADCFilterCfg.ADCAvgNum = AppIMPCfg.ADCAvgNum;
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ; /* Tell filter block clock rate of ADC*/
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
dsp_cfg.DftCfg.DftNum = AppIMPCfg.DftNum;
dsp_cfg.DftCfg.DftSrc = AppIMPCfg.DftSrc;
dsp_cfg.DftCfg.HanWinEn = AppIMPCfg.HanWinEn;
memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg));
AD5940_DSPCfgS(&dsp_cfg);
/* Enable all of them. They are automatically turned off during hibernate mode to save power */
if(AppIMPCfg.BiasVolt == 0.0f)
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
else
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH|AFECTRL_DCBUFPWR, bTRUE);
/* 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)
{
AppIMPCfg.InitSeqInfo.SeqId = SEQID_1;
AppIMPCfg.InitSeqInfo.SeqRamAddr = AppIMPCfg.SeqStartAddr;
AppIMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.InitSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppIMPCfg.InitSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
static AD5940Err AppIMPSeqMeasureGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
uint32_t WaitClks;
SWMatrixCfg_Type sw_cfg;
ClksCalInfo_Type clks_cal;
LPAmpCfg_Type LpAmpCfg;
/* Calculate number of clocks to get data to FIFO */
clks_cal.DataType = DATATYPE_DFT;
clks_cal.DftSrc = AppIMPCfg.DftSrc;
clks_cal.DataCount = 1L<<(AppIMPCfg.DftNum+2); /* 2^(DFTNUMBER+2) */
clks_cal.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
clks_cal.ADCAvgNum = AppIMPCfg.ADCAvgNum;
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
/* Start Sequence Generator */
AD5940_SEQGenCtrl(bTRUE);
AD5940_SEQGpioCtrlS(AGPIO_Pin2); /* Set GPIO1, clear others that under control */
AD5940_SEQGenInsert(SEQ_WAIT(16*250)); /* @todo wait 250us? */
/* Disconnect SE0 from LPTIA*/
LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
LpAmpCfg.LpPaPwrEn = bTRUE;
LpAmpCfg.LpTiaPwrEn = bTRUE;
LpAmpCfg.LpTiaRf = AppIMPCfg.LpTiaRf;
LpAmpCfg.LpTiaRload = AppIMPCfg.LpTiaRl;
LpAmpCfg.LpTiaRtia = LPTIARTIA_OPEN; /* Disconnect Rtia to avoid RC filter discharge */
LpAmpCfg.LpTiaSW = LPTIASW(7)|LPTIASW(8)|LPTIASW(12)|LPTIASW(13);
AD5940_LPAMPCfgS(&LpAmpCfg);
/* Sensor + Rload Measurement */
sw_cfg.Dswitch = AppIMPCfg.DswitchSel;
sw_cfg.Pswitch = AppIMPCfg.PswitchSel;
sw_cfg.Nswitch = AppIMPCfg.NswitchSel;
sw_cfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
AD5940_SWMatrixCfgS(&sw_cfg);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_SINC2NOTCH, bTRUE); /* Enable Waveform generator */
//delay for signal settling DFT_WAIT
AD5940_SEQGenInsert(SEQ_WAIT(16*10));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks));
//wait for first data ready
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH|AFECTRL_DFT|AFECTRL_ADCCNV, bFALSE);
/* RLOAD Measurement */
sw_cfg.Dswitch = SWD_SE0;
sw_cfg.Pswitch = SWP_SE0;
sw_cfg.Nswitch = SWN_SE0LOAD;
sw_cfg.Tswitch = SWT_SE0LOAD|SWT_TRTIA;
AD5940_SWMatrixCfgS(&sw_cfg);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|AFECTRL_SINC2NOTCH, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); //delay for signal settling DFT_WAIT
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH|AFECTRL_ADCCNV, bFALSE);
/* RCAL Measurement */
sw_cfg.Dswitch = SWD_RCAL0;
sw_cfg.Pswitch = SWP_RCAL0;
sw_cfg.Nswitch = SWN_RCAL1;
sw_cfg.Tswitch = SWT_RCAL1|SWT_TRTIA;
AD5940_SWMatrixCfgS(&sw_cfg);
/* Reconnect LP loop */
LpAmpCfg.LpTiaRtia = AppIMPCfg.LptiaRtiaSel; /* Disconnect Rtia to avoid RC filter discharge */
LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
AD5940_LPAMPCfgS(&LpAmpCfg);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|AFECTRL_SINC2NOTCH, bTRUE);
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); //delay for signal settling DFT_WAIT
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT/*|AFECTRL_SINC2NOTCH*/, bTRUE); /* Start ADC convert and DFT */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bFALSE);
AD5940_SEQGpioCtrlS(0); /* Clr GPIO1 */
sw_cfg.Dswitch = SWD_OPEN;
sw_cfg.Pswitch = SWP_OPEN;
sw_cfg.Nswitch = SWN_OPEN;
sw_cfg.Tswitch = SWT_OPEN;
AD5940_SWMatrixCfgS(&sw_cfg);
//AD5940_EnterSleepS();/* Goto hibernate */
/* Sequence end. */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
if(error == AD5940ERR_OK)
{
AppIMPCfg.MeasureSeqInfo.SeqId = SEQID_0;
AppIMPCfg.MeasureSeqInfo.SeqRamAddr = AppIMPCfg.InitSeqInfo.SeqRamAddr + AppIMPCfg.InitSeqInfo.SeqLen ;
AppIMPCfg.MeasureSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.MeasureSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppIMPCfg.MeasureSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
/* This function provide application initialize. It can also enable Wupt that will automatically trigger sequence. Or it can configure */
int32_t AppIMPInit(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 = bTRUE;
seq_cfg.SeqCntCRCClr = bTRUE;
seq_cfg.SeqEnable = bFALSE;
seq_cfg.SeqWrTimer = 0;
AD5940_SEQCfg(&seq_cfg);
/* Reconfigure FIFO */
AD5940_FIFOCtrlS(FIFOSRC_DFT, 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 = FIFOSRC_DFT;
fifo_cfg.FIFOThresh = AppIMPCfg.FifoThresh; /* DFT result. One pair for RCAL, another for Rz. One DFT result have real part and imaginary part */
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
/* Start sequence generator */
/* Initialize sequencer generator */
if((AppIMPCfg.IMPInited == bFALSE)||\
(AppIMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
AD5940_SEQGenInit(pBuffer, BufferSize);
/* Generate initialize sequence */
error = AppIMPSeqCfgGen(); /* Application initialization sequence using either MCU or sequencer */
if(error != AD5940ERR_OK) return error;
/* Generate measurement sequence */
error = AppIMPSeqMeasureGen();
if(error != AD5940ERR_OK) return error;
AppIMPCfg.bParaChanged = bFALSE; /* Clear this flag as we already implemented the new configuration */
}
/* Initialization sequencer */
AppIMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.InitSeqInfo);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg); /* Enable sequencer */
AD5940_SEQMmrTrig(AppIMPCfg.InitSeqInfo.SeqId);
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
/* Measurement sequence */
AppIMPCfg.MeasureSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.MeasureSeqInfo);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg); /* Enable sequencer, and wait for trigger */
AD5940_ClrMCUIntFlag(); /* Clear interrupt flag generated before */
AD5940_AFEPwrBW(AppIMPCfg.PwrMod, AFEBW_250KHZ);
AD5940_WriteReg(REG_AFE_LPTIASW0, 0x3180);
AppIMPCfg.IMPInited = bTRUE; /* IMP application has been initialized. */
return AD5940ERR_OK;
}
/* Modify registers when AFE wakeup */
int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
{
if(AppIMPCfg.NumOfData > 0)
{
AppIMPCfg.FifoDataCount += *pDataCount/4;
if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
}
if(AppIMPCfg.StopRequired == bTRUE)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
if(AppIMPCfg.SweepCfg.SweepEn) /* Need to set new frequency and set power mode */
{
/* Check frequency and update FIlter settings */
AD5940_WGFreqCtrlS(AppIMPCfg.SweepNextFreq, AppIMPCfg.SysClkFreq);
}
return AD5940ERR_OK;
}
/* Depending on the data type, do appropriate data pre-process before return back to controller */
int32_t AppIMPDataProcess(int32_t * const pData, uint32_t *pDataCount)
{
uint32_t DataCount = *pDataCount;
uint32_t ImpResCount = DataCount/6;
fImpPol_Type * const pOut = (fImpPol_Type*)pData;
iImpCar_Type * pSrcData = (iImpCar_Type*)pData;
*pDataCount = 0;
DataCount = (DataCount/6)*6;/* We expect Rz+Rload, Rload and RCAL data, . One DFT result has two data in FIFO, real part and imaginary part. */
/* Convert DFT result to int32_t type */
for(uint32_t i=0; i<DataCount; i++)
{
pData[i] &= 0x3ffff;
if(pData[i]&(1L<<17)) /* Bit17 is sign bit */
{
pData[i] |= 0xfffc0000; /* Data is 18bit in two's complement, bit17 is the sign bit */
}
}
for(uint32_t i=0; i<ImpResCount; i++)
{
if(1)
{
fImpCar_Type DftRcal, DftRzRload, DftRload, temp1, temp2, res;
fImpCar_Type DftConst1 = {1.0f, 0};
/*
The sign of DFT Image result is added '-1' by hardware. Fix it below.
*/
DftRzRload.Real = pSrcData->Real;
DftRzRload.Image = -pSrcData->Image;
pSrcData++;
DftRload.Real = pSrcData->Real;
DftRload.Image = -pSrcData->Image;
pSrcData++;
DftRcal.Real = pSrcData->Real;
DftRcal.Image = -pSrcData->Image;
pSrcData++;
/**
Rz = RloadRz - Rload
RloadRz = DftRcal/DftRzRload*RCAL;
Rload = DftRcal/DftRload*RCAL;
Rz = RloadRz - Rload =
(1/DftRzRload - 1/DftRload)*DftRcal*RCAL;
where RCAL is the RCAL resistor value in Ohm.
*/
//temp1 = 1/DftRzRload;
//temp2 = 1/DftRload;
temp1 = AD5940_ComplexDivFloat(&DftConst1, &DftRzRload);
temp2 = AD5940_ComplexDivFloat(&DftConst1, &DftRload);
res = AD5940_ComplexSubFloat(&temp1, &temp2);
res = AD5940_ComplexMulFloat(&res, &DftRcal);
pOut[i].Magnitude = AD5940_ComplexMag(&res)*AppIMPCfg.RcalVal;
pOut[i].Phase = AD5940_ComplexPhase(&res);
}
else
{
iImpCar_Type *pDftRcal, *pDftRzRload, *pDftRload;
pDftRzRload = pSrcData++;
pDftRload = pSrcData++;
pDftRcal = pSrcData++;
float RzRloadMag, RzRloadPhase;
float RloadMag, RloadPhase;
float RzMag,RzPhase;
float RcalMag, RcalPhase;
float RzReal, RzImage;
RzReal = pDftRload->Real - pDftRzRload->Real;
RzImage = pDftRload->Image - pDftRzRload->Image;
RzRloadMag = sqrt((float)pDftRzRload->Real*pDftRzRload->Real+(float)pDftRzRload->Image*pDftRzRload->Image);
RzRloadPhase = atan2(-pDftRzRload->Image,pDftRzRload->Real);
RcalMag = sqrt((float)pDftRcal->Real*pDftRcal->Real+(float)pDftRcal->Image*pDftRcal->Image);
RcalPhase = atan2(-pDftRcal->Image,pDftRcal->Real);
RzMag = sqrt((float)RzReal*RzReal+(float)RzImage*RzImage);
RzPhase = atan2(-RzImage,RzReal);
RloadMag = sqrt((float)pDftRload->Real*pDftRload->Real+(float)pDftRload->Image*pDftRload->Image);
RloadPhase = atan2(-pDftRload->Image,pDftRload->Real);
RzMag = (AppIMPCfg.RcalVal*RcalMag*RzMag)/(RzRloadMag*RloadMag);
RzPhase = -(RcalPhase + RzPhase - RloadPhase - RzRloadPhase);
// RzPhase = (RcalPhase + RzPhase - RloadPhase - RzRloadPhase);
pOut[i].Magnitude = RzMag;
pOut[i].Phase = RzPhase;
}
}
*pDataCount = ImpResCount;
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
/* Calculate next frequency point */
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepNextFreq;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
}
return 0;
}
/**
*/
int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
{
uint32_t BuffCount;
uint32_t FifoCnt;
BuffCount = *pCount;
*pCount = 0;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
AD5940_SleepKeyCtrlS(SLPKEY_LOCK); /* Prohibit AFE to enter sleep mode. */
if(AD5940_INTCTestFlag(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH) == bTRUE)
{
/* Now there should be 4 data in FIFO */
FifoCnt = (AD5940_FIFOGetCnt()/6)*6;
if(FifoCnt > BuffCount)
{
///@todo buffer is limited.
}
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AppIMPRegModify(pBuff, &FifoCnt); /* If there is need to do AFE re-configure, do it here when AFE is in active state */
//AD5940_EnterSleepS(); /* Manually put AFE back to hibernate mode. This operation only takes effect when register value is ACTIVE previously */
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); /* Allow AFE to enter sleep mode. */
/* Process data */
AppIMPDataProcess((int32_t*)pBuff,&FifoCnt);
*pCount = FifoCnt;
return 0;
}
return 0;
}

View File

@ -1,91 +0,0 @@
/*!
*****************************************************************************
@file: Impedance.h
@author: Neo Xu
@brief: Electrochemical impedance spectroscopy based on example AD5940_Impedance
-----------------------------------------------------------------------------
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 _IMPEDANCESEQUENCES_H_
#define _IMPEDANCESEQUENCES_H_
#include "AD5940.H"
#include <stdio.h>
#include "string.h"
#include "math.h"
typedef struct
{
/* Common configurations for all kinds of Application. */
BoolFlag bParaChanged; /* Indicate to generate sequence again. It's auto cleared by AppBIAInit */
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 */
float ImpODR; /* */
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 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 SysClkFreq; /* The real frequency of system clock */
float AdcClkFreq; /* The real frequency of ADC clock */
float RcalVal; /* Rcal value in Ohm */
/* Switch Configuration */
uint32_t DswitchSel;
uint32_t PswitchSel;
uint32_t NswitchSel;
uint32_t TswitchSel;
uint32_t PwrMod; /* Control Chip power mode(LP/HP) */
uint32_t HstiaRtiaSel; /* 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 ExcitBufGain; /* Select from EXCTBUFGAIN_2, EXCTBUFGAIN_0P25 */
uint32_t HsDacGain; /* Select from HSDACGAIN_1, HSDACGAIN_0P2 */
uint32_t HsDacUpdateRate;
float DacVoltPP; /* DAC output voltage in mV peak to peak. Maximum value is 600mVpp. Peak to peak voltage */
float BiasVolt; /* The excitation signal is DC+AC. This parameter decides the DC value in mV unit. 0.0mV means no DC bias.*/
float SinFreq; /* Frequency of excitation signal */
uint32_t DftNum; /* DFT number */
uint32_t DftSrc; /* DFT Source */
BoolFlag HanWinEn; /* Enable Hanning window */
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;
uint8_t ADCSinc2Osr;
uint8_t ADCAvgNum;
uint8_t ADC_Rate;
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 */
float Vzero; /* Voltage on SE0 pin and Vzero, optimumly 1100mV*/
float Vbias; /* Voltage on CE0 and PA */
/* Sweep Function Control */
SoftSweepCfg_Type SweepCfg;
uint32_t FifoThresh; /* FIFO threshold. Should be N*4 */
/* Private variables for internal usage */
/* Private variables for internal usage */
float SweepCurrFreq;
float SweepNextFreq;
float FreqofData; /* The frequency of latest data sampled */
BoolFlag IMPInited; /* 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 */
}AppIMPCfg_Type;
#define IMPCTRL_START 0
#define IMPCTRL_STOPNOW 1
#define IMPCTRL_STOPSYNC 2
#define IMPCTRL_GETFREQ 3 /* Get Current frequency of returned data from ISR */
#define IMPCTRL_SHUTDOWN 4 /* Note: shutdown here means turn off everything and put AFE to hibernate mode. The word 'SHUT DOWN' is only used here. */
int32_t AppIMPInit(uint32_t *pBuffer, uint32_t BufferSize);
int32_t AppIMPGetCfg(void *pCfg);
int32_t AppIMPISR(void *pBuff, uint32_t *pCount);
int32_t AppIMPCtrl(uint32_t Command, void *pPara);
#endif

View File

@ -1,740 +0,0 @@
/*!
*****************************************************************************
@file: Impedance.c
@author: Neo Xu
@brief: standard 4-wire or 2-wire impedance measurement sequences.
-----------------------------------------------------------------------------
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 "ad5940.h"
#include <stdio.h>
#include "string.h"
#include "math.h"
#include "Impedance.h"
/* Default LPDAC resolution(2.5V internal reference). */
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
/*
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
*/
AppIMPCfg_Type AppIMPCfg =
{
.bParaChanged = bFALSE,
.SeqStartAddr = 0,
.MaxSeqLen = 0,
.SeqStartAddrCal = 0,
.MaxSeqLenCal = 0,
.ImpODR = 0.0001, /* 20.0 Hz*/
.NumOfData = -1,
.SysClkFreq = 16000000.0,
.WuptClkFreq = 32000.0,
.AdcClkFreq = 16000000.0,
.RcalVal = 10000.0,
.DswitchSel = SWD_CE0,
.PswitchSel = SWP_CE0,
.NswitchSel = SWN_AIN1,
.TswitchSel = SWT_AIN1,
.PwrMod = AFEPWR_HP,
.HstiaRtiaSel = HSTIARTIA_5K,
.ExtRtia = 0, //set only when HstiaRtiaSel = HSTIARTIA_OPEN
.ExcitBufGain = EXCITBUFGAIN_0P25,//EXCITBUFGAIN_2,
.HsDacGain = HSDACGAIN_0P2,//HSDACGAIN_1,
.HsDacUpdateRate = 7,
.DacVoltPP = 800.0,
.BiasVolt = -0.0f,
.SinFreq = 100000.0, /* 1000Hz */
.DftNum = DFTNUM_16384,
.DftSrc = DFTSRC_SINC3,
.HanWinEn = bTRUE,
.AdcPgaGain = ADCPGA_4,//ADCPGA_1,
.ADCSinc3Osr = ADCSINC3OSR_2,
.ADCSinc2Osr = ADCSINC2OSR_22,
.ADCAvgNum = ADCAVGNUM_16,
.SweepCfg.SweepEn = bTRUE,
.SweepCfg.SweepStart = 1000,
.SweepCfg.SweepStop = 100000.0,
.SweepCfg.SweepPoints = 101,
.SweepCfg.SweepLog = bFALSE,
.SweepCfg.SweepIndex = 0,
.FifoThresh = 4,
.IMPInited = bFALSE,
.StopRequired = bFALSE,
};
/**
This function is provided for upper controllers that want to change
application parameters specially for user defined parameters.
*/
int32_t AppIMPGetCfg(void *pCfg)
{
if(pCfg)
{
*(AppIMPCfg_Type**)pCfg = &AppIMPCfg;
return AD5940ERR_OK;
}
return AD5940ERR_PARA;
}
int32_t AppIMPCtrl(uint32_t Command, void *pPara)
{
switch (Command)
{
case IMPCTRL_START:
{
WUPTCfg_Type wupt_cfg;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
if(AppIMPCfg.IMPInited == 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;
wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppIMPCfg.WuptClkFreq/AppIMPCfg.ImpODR)-4;
AD5940_WUPTCfg(&wupt_cfg);
AppIMPCfg.FifoDataCount = 0; /* restart */
break;
}
case IMPCTRL_STOPNOW:
{
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
/* 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 IMPCTRL_STOPSYNC:
{
AppIMPCfg.StopRequired = bTRUE;
break;
}
case IMPCTRL_GETFREQ:
{
if(pPara == 0)
return AD5940ERR_PARA;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
*(float*)pPara = AppIMPCfg.FreqofData;
else
*(float*)pPara = AppIMPCfg.SinFreq;
}
break;
case IMPCTRL_SHUTDOWN:
{
AppIMPCtrl(IMPCTRL_STOPNOW, 0); /* Stop the measurement if it's running. */
/* Turn off LPloop related blocks which are not controlled automatically by hibernate 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;
}
/* generated code snnipet */
float AppIMPGetCurrFreq(void)
{
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
return AppIMPCfg.FreqofData;
else
return AppIMPCfg.SinFreq;
}
/* Application initialization */
static AD5940Err AppIMPSeqCfgGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
HSLoopCfg_Type HsLoopCfg;
DSPCfg_Type dsp_cfg;
float sin_freq;
/* 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 = bFALSE;
aferef_cfg.Lp1V8BuffEn = bFALSE;
/* LP reference control - turn off them to save power*/
if(AppIMPCfg.BiasVolt != 0.0f) /* With bias voltage */
{
aferef_cfg.LpBandgapEn = bTRUE;
aferef_cfg.LpRefBufEn = bTRUE;
}
else
{
aferef_cfg.LpBandgapEn = bFALSE;
aferef_cfg.LpRefBufEn = bFALSE;
}
aferef_cfg.LpRefBoostEn = bFALSE;
AD5940_REFCfgS(&aferef_cfg);
HsLoopCfg.HsDacCfg.ExcitBufGain = AppIMPCfg.ExcitBufGain;
HsLoopCfg.HsDacCfg.HsDacGain = AppIMPCfg.HsDacGain;
HsLoopCfg.HsDacCfg.HsDacUpdateRate = AppIMPCfg.HsDacUpdateRate;
HsLoopCfg.HsTiaCfg.DiodeClose = bFALSE;
if(AppIMPCfg.BiasVolt != 0.0f) /* With bias voltage */
HsLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_VZERO0;
else
HsLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
HsLoopCfg.HsTiaCfg.HstiaCtia = 31; /* 31pF + 2pF */
HsLoopCfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
HsLoopCfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
HsLoopCfg.HsTiaCfg.HstiaRtiaSel = AppIMPCfg.HstiaRtiaSel;
HsLoopCfg.SWMatCfg.Dswitch = AppIMPCfg.DswitchSel;
HsLoopCfg.SWMatCfg.Pswitch = AppIMPCfg.PswitchSel;
HsLoopCfg.SWMatCfg.Nswitch = AppIMPCfg.NswitchSel;
HsLoopCfg.SWMatCfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
HsLoopCfg.WgCfg.WgType = WGTYPE_SIN;
HsLoopCfg.WgCfg.GainCalEn = bTRUE;
HsLoopCfg.WgCfg.OffsetCalEn = bTRUE;
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCfg.SweepStart;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepCfg.SweepStart;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
sin_freq = AppIMPCfg.SweepCurrFreq;
}
else
{
sin_freq = AppIMPCfg.SinFreq;
AppIMPCfg.FreqofData = sin_freq;
}
HsLoopCfg.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(sin_freq, AppIMPCfg.SysClkFreq);
HsLoopCfg.WgCfg.SinCfg.SinAmplitudeWord = (uint32_t)(AppIMPCfg.DacVoltPP/800.0f*2047 + 0.5f);
HsLoopCfg.WgCfg.SinCfg.SinOffsetWord = 0;
HsLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
AD5940_HSLoopCfgS(&HsLoopCfg);
if(AppIMPCfg.BiasVolt != 0.0f) /* With bias voltage */
{
LPDACCfg_Type lpdac_cfg;
lpdac_cfg.LpdacSel = LPDAC0;
lpdac_cfg.LpDacVbiasMux = LPDACVBIAS_12BIT; /* Use Vbias to tuning BiasVolt. */
lpdac_cfg.LpDacVzeroMux = LPDACVZERO_6BIT; /* Vbias-Vzero = BiasVolt */
lpdac_cfg.DacData6Bit = 0x40>>1; /* Set Vzero to middle scale. */
if(AppIMPCfg.BiasVolt<-1100.0f) AppIMPCfg.BiasVolt = -1100.0f + DAC12BITVOLT_1LSB;
if(AppIMPCfg.BiasVolt> 1100.0f) AppIMPCfg.BiasVolt = 1100.0f - DAC12BITVOLT_1LSB;
lpdac_cfg.DacData12Bit = (uint32_t)((AppIMPCfg.BiasVolt + 1100.0f)/DAC12BITVOLT_1LSB);
lpdac_cfg.DataRst = bFALSE; /* Do not reset data register */
lpdac_cfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VBIAS2PIN|LPDACSW_VZERO2LPTIA|LPDACSW_VZERO2PIN|LPDACSW_VZERO2HSTIA;
lpdac_cfg.LpDacRef = LPDACREF_2P5;
lpdac_cfg.LpDacSrc = LPDACSRC_MMR; /* Use MMR data, we use LPDAC to generate bias voltage for LPTIA - the Vzero */
lpdac_cfg.PowerEn = bTRUE; /* Power up LPDAC */
AD5940_LPDACCfgS(&lpdac_cfg);
}
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
dsp_cfg.ADCBaseCfg.ADCPga = AppIMPCfg.AdcPgaGain;
memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
dsp_cfg.ADCFilterCfg.ADCAvgNum = AppIMPCfg.ADCAvgNum;
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ; /* Tell filter block clock rate of ADC*/
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
dsp_cfg.DftCfg.DftNum = AppIMPCfg.DftNum;
dsp_cfg.DftCfg.DftSrc = AppIMPCfg.DftSrc;
dsp_cfg.DftCfg.HanWinEn = AppIMPCfg.HanWinEn;
memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg));
AD5940_DSPCfgS(&dsp_cfg);
/* Enable all of them. They are automatically turned off during hibernate mode to save power */
if(AppIMPCfg.BiasVolt == 0.0f)
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
else
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH|AFECTRL_DCBUFPWR, bTRUE);
/* 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)
{
AppIMPCfg.InitSeqInfo.SeqId = SEQID_1;
AppIMPCfg.InitSeqInfo.SeqRamAddr = AppIMPCfg.SeqStartAddr;
AppIMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.InitSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppIMPCfg.InitSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
static AD5940Err AppIMPSeqMeasureGen(void)
{
AD5940Err error = AD5940ERR_OK;
const uint32_t *pSeqCmd;
uint32_t SeqLen;
uint32_t WaitClks;
SWMatrixCfg_Type sw_cfg;
ClksCalInfo_Type clks_cal;
clks_cal.DataType = DATATYPE_DFT;
clks_cal.DftSrc = AppIMPCfg.DftSrc;
clks_cal.DataCount = 1L<<(AppIMPCfg.DftNum+2); /* 2^(DFTNUMBER+2) */
clks_cal.ADCSinc2Osr = AppIMPCfg.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = AppIMPCfg.ADCSinc3Osr;
clks_cal.ADCAvgNum = AppIMPCfg.ADCAvgNum;
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
AD5940_SEQGenCtrl(bTRUE);
AD5940_SEQGpioCtrlS(AGPIO_Pin2); /* Set GPIO1, clear others that under control */
AD5940_SEQGenInsert(SEQ_WAIT(16*250)); /* @todo wait 250us? */
sw_cfg.Dswitch = SWD_RCAL0;
sw_cfg.Pswitch = SWP_RCAL0;
sw_cfg.Nswitch = SWN_RCAL1;
sw_cfg.Tswitch = SWT_RCAL1|SWT_TRTIA;
AD5940_SWMatrixCfgS(&sw_cfg);
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bTRUE);
AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE); /* Enable Waveform generator */
//delay for signal settling DFT_WAIT
AD5940_SEQGenInsert(SEQ_WAIT(16*10));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[0]); /* Record the start address of the next command. */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
//wait for first data ready
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bFALSE); /* Stop ADC convert and DFT */
/* Configure matrix for external Rz */
sw_cfg.Dswitch = AppIMPCfg.DswitchSel;
sw_cfg.Pswitch = AppIMPCfg.PswitchSel;
sw_cfg.Nswitch = AppIMPCfg.NswitchSel;
sw_cfg.Tswitch = SWT_TRTIA|AppIMPCfg.TswitchSel;
AD5940_SWMatrixCfgS(&sw_cfg);
AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE); /* Enable Waveform generator */
AD5940_SEQGenInsert(SEQ_WAIT(16*10)); //delay for signal settling DFT_WAIT
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */
AD5940_SEQGenFetchSeq(NULL, &AppIMPCfg.SeqWaitAddr[1]); /* Record the start address of next command */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks/2));
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */
AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
AFECTRL_WG|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
AFECTRL_SINC2NOTCH, bFALSE);
AD5940_SEQGpioCtrlS(0); /* Clr GPIO1 */
AD5940_EnterSleepS();/* Goto hibernate */
/* Sequence end. */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
if(error == AD5940ERR_OK)
{
AppIMPCfg.MeasureSeqInfo.SeqId = SEQID_0;
AppIMPCfg.MeasureSeqInfo.SeqRamAddr = AppIMPCfg.InitSeqInfo.SeqRamAddr + AppIMPCfg.InitSeqInfo.SeqLen ;
AppIMPCfg.MeasureSeqInfo.pSeqCmd = pSeqCmd;
AppIMPCfg.MeasureSeqInfo.SeqLen = SeqLen;
/* Write command to SRAM */
AD5940_SEQCmdWrite(AppIMPCfg.MeasureSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
}
else
return error; /* Error */
return AD5940ERR_OK;
}
/* Depending on frequency of Sin wave set optimum filter settings */
AD5940Err AppIMPCheckFreq(float freq)
{
ADCFilterCfg_Type filter_cfg;
DFTCfg_Type dft_cfg;
HSDACCfg_Type hsdac_cfg;
uint32_t WaitClks;
ClksCalInfo_Type clks_cal;
FreqParams_Type freq_params;
uint32_t SeqCmdBuff[32];
uint32_t SRAMAddr = 0;;
/* Step 1: Check Frequency */
freq_params = AD5940_GetFreqParameters(freq);
if(freq < 0.51)
{
/* Update HSDAC update rate */
hsdac_cfg.ExcitBufGain =EXCITBUFGAIN_2;// AppIMPCfg.ExcitBufGain;
hsdac_cfg.HsDacGain = HSDACGAIN_1;//AppIMPCfg.HsDacGain;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_40K); //set as per load current range
/*Update ADC rate */
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
/* Change clock to 16MHz oscillator */
AD5940_HPModeEn(bFALSE);
}
else if(freq < 5 )
{
/* Update HSDAC update rate */
hsdac_cfg.ExcitBufGain =EXCITBUFGAIN_2;// AppIMPCfg.ExcitBufGain;
hsdac_cfg.HsDacGain = HSDACGAIN_1;//AppIMPCfg.HsDacGain;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_40K); //set as per load current range
/*Update ADC rate */
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
/* Change clock to 16MHz oscillator */
AD5940_HPModeEn(bFALSE);
}else if(freq < 450)
{
/* Update HSDAC update rate */
hsdac_cfg.ExcitBufGain =AppIMPCfg.ExcitBufGain;
hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_5K); //set as per load current range
/*Update ADC rate */
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
/* Change clock to 16MHz oscillator */
AD5940_HPModeEn(bFALSE);
}
else if(freq<80000)
{
/* Update HSDAC update rate */
hsdac_cfg.ExcitBufGain =AppIMPCfg.ExcitBufGain;
hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_5K); //set as per load current range
/*Update ADC rate */
filter_cfg.ADCRate = ADCRATE_800KHZ;
AppIMPCfg.AdcClkFreq = 16e6;
/* Change clock to 16MHz oscillator */
AD5940_HPModeEn(bFALSE);
}
/* High power mode */
if(freq >= 80000)
{
/* Update HSDAC update rate */
hsdac_cfg.ExcitBufGain =AppIMPCfg.ExcitBufGain;
hsdac_cfg.HsDacGain = AppIMPCfg.HsDacGain;
hsdac_cfg.HsDacUpdateRate = 0x07;
AD5940_HSDacCfgS(&hsdac_cfg);
AD5940_HSRTIACfgS(HSTIARTIA_5K); //set as per load current range
/*Update ADC rate */
filter_cfg.ADCRate = ADCRATE_1P6MHZ;
AppIMPCfg.AdcClkFreq = 32e6;
/* Change clock to 32MHz oscillator */
AD5940_HPModeEn(bTRUE);
}
/* Step 2: Adjust ADCFILTERCON and DFTCON to set optimumn SINC3, SINC2 and DFTNUM settings */
filter_cfg.ADCAvgNum = ADCAVGNUM_16; /* Don't care because it's disabled */
filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr;
filter_cfg.ADCSinc3Osr = freq_params.ADCSinc3Osr;
filter_cfg.BpSinc3 = bFALSE;
filter_cfg.BpNotch = bTRUE;
filter_cfg.Sinc2NotchEnable = bTRUE;
dft_cfg.DftNum = freq_params.DftNum;
dft_cfg.DftSrc = freq_params.DftSrc;
dft_cfg.HanWinEn = AppIMPCfg.HanWinEn;
AD5940_ADCFilterCfgS(&filter_cfg);
AD5940_DFTCfgS(&dft_cfg);
/* Step 3: Calculate clocks needed to get result to FIFO and update sequencer wait command */
clks_cal.DataType = DATATYPE_DFT;
clks_cal.DftSrc = freq_params.DftSrc;
clks_cal.DataCount = 1L<<(freq_params.DftNum+2); /* 2^(DFTNUMBER+2) */
clks_cal.ADCSinc2Osr = freq_params.ADCSinc2Osr;
clks_cal.ADCSinc3Osr = freq_params.ADCSinc3Osr;
clks_cal.ADCAvgNum = 0;
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
SRAMAddr = AppIMPCfg.MeasureSeqInfo.SeqRamAddr + AppIMPCfg.SeqWaitAddr[0];
SeqCmdBuff[0] =SEQ_WAIT(WaitClks/2);
SeqCmdBuff[1] =SEQ_WAIT(WaitClks/2);
AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 2);
SRAMAddr = AppIMPCfg.MeasureSeqInfo.SeqRamAddr + AppIMPCfg.SeqWaitAddr[1];
SeqCmdBuff[0] =SEQ_WAIT(WaitClks/2);
SeqCmdBuff[1] =SEQ_WAIT(WaitClks/2);
AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 2);
return AD5940ERR_OK;
}
/* This function provide application initialize. It can also enable Wupt that will automatically trigger sequence. Or it can configure */
int32_t AppIMPInit(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 = bTRUE;
seq_cfg.SeqCntCRCClr = bTRUE;
seq_cfg.SeqEnable = bFALSE;
seq_cfg.SeqWrTimer = 0;
AD5940_SEQCfg(&seq_cfg);
/* Reconfigure FIFO */
AD5940_FIFOCtrlS(FIFOSRC_DFT, 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 = FIFOSRC_DFT;
fifo_cfg.FIFOThresh = AppIMPCfg.FifoThresh; /* DFT result. One pair for RCAL, another for Rz. One DFT result have real part and imaginary part */
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
/* Start sequence generator */
/* Initialize sequencer generator */
if((AppIMPCfg.IMPInited == bFALSE)||\
(AppIMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
AD5940_SEQGenInit(pBuffer, BufferSize);
/* Generate initialize sequence */
error = AppIMPSeqCfgGen(); /* Application initialization sequence using either MCU or sequencer */
if(error != AD5940ERR_OK) return error;
/* Generate measurement sequence */
error = AppIMPSeqMeasureGen();
if(error != AD5940ERR_OK) return error;
AppIMPCfg.bParaChanged = bFALSE; /* Clear this flag as we already implemented the new configuration */
}
/* Initialization sequencer */
AppIMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.InitSeqInfo);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg); /* Enable sequencer */
AD5940_SEQMmrTrig(AppIMPCfg.InitSeqInfo.SeqId);
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
/* Measurement sequence */
AppIMPCfg.MeasureSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppIMPCfg.MeasureSeqInfo);
AppIMPCheckFreq(AppIMPCfg.FreqofData);
seq_cfg.SeqEnable = bTRUE;
AD5940_SEQCfg(&seq_cfg); /* Enable sequencer, and wait for trigger */
AD5940_ClrMCUIntFlag(); /* Clear interrupt flag generated before */
//AD5940_AFEPwrBW(AppIMPCfg.PwrMod, AFEBW_250KHZ);
AppIMPCfg.IMPInited = bTRUE; /* IMP application has been initialized. */
return AD5940ERR_OK;
}
/* Modify registers when AFE wakeup */
int32_t AppIMPRegModify(int32_t * const pData, uint32_t *pDataCount)
{
if(AppIMPCfg.NumOfData > 0)
{
AppIMPCfg.FifoDataCount += *pDataCount/4;
if(AppIMPCfg.FifoDataCount >= AppIMPCfg.NumOfData)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
}
if(AppIMPCfg.StopRequired == bTRUE)
{
AD5940_WUPTCtrl(bFALSE);
return AD5940ERR_OK;
}
if(AppIMPCfg.SweepCfg.SweepEn) /* Need to set new frequency and set power mode */
{
AD5940_WGFreqCtrlS(AppIMPCfg.SweepNextFreq, AppIMPCfg.SysClkFreq);
AppIMPCheckFreq(AppIMPCfg.SweepNextFreq);
}
return AD5940ERR_OK;
}
/* Depending on the data type, do appropriate data pre-process before return back to controller */
int32_t AppIMPDataProcess(int32_t * const pData, uint32_t *pDataCount)
{
uint32_t DataCount = *pDataCount;
uint32_t ImpResCount = DataCount/4;
fImpPol_Type * const pOut = (fImpPol_Type*)pData;
iImpCar_Type * pSrcData = (iImpCar_Type*)pData;
*pDataCount = 0;
DataCount = (DataCount/4)*4;/* We expect RCAL data together with Rz data. One DFT result has two data in FIFO, real part and imaginary part. */
/* Convert DFT result to int32_t type */
for(uint32_t i=0; i<DataCount; i++)
{
pData[i] &= 0x3ffff; /* @todo option to check ECC */
if(pData[i]&(1L<<17)) /* Bit17 is sign bit */
{
pData[i] |= 0xfffc0000; /* Data is 18bit in two's complement, bit17 is the sign bit */
}
}
for(uint32_t i=0; i<ImpResCount; i++)
{
iImpCar_Type *pDftRcal, *pDftRz;
pDftRcal = pSrcData++;
pDftRz = pSrcData++;
float RzMag,RzPhase;
float RcalMag, RcalPhase;
RcalMag = sqrt((float)pDftRcal->Real*pDftRcal->Real+(float)pDftRcal->Image*pDftRcal->Image);
RcalPhase = atan2(-pDftRcal->Image,pDftRcal->Real);
RzMag = sqrt((float)pDftRz->Real*pDftRz->Real+(float)pDftRz->Image*pDftRz->Image);
RzPhase = atan2(-pDftRz->Image,pDftRz->Real);
RzMag = RcalMag/RzMag*AppIMPCfg.RcalVal;
RzPhase = RcalPhase - RzPhase;
//printf("V:%d,%d,I:%d,%d ",pDftRcal->Real,pDftRcal->Image, pDftRz->Real, pDftRz->Image);
pOut[i].Magnitude = RzMag;
pOut[i].Phase = RzPhase;
}
*pDataCount = ImpResCount;
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
/* Calculate next frequency point */
if(AppIMPCfg.SweepCfg.SweepEn == bTRUE)
{
AppIMPCfg.FreqofData = AppIMPCfg.SweepCurrFreq;
AppIMPCfg.SweepCurrFreq = AppIMPCfg.SweepNextFreq;
AD5940_SweepNext(&AppIMPCfg.SweepCfg, &AppIMPCfg.SweepNextFreq);
}
return 0;
}
/**
*/
int32_t AppIMPISR(void *pBuff, uint32_t *pCount)
{
uint32_t BuffCount;
uint32_t FifoCnt;
BuffCount = *pCount;
*pCount = 0;
if(AD5940_WakeUp(10) > 10) /* Wakeup AFE by read register, read 10 times at most */
return AD5940ERR_WAKEUP; /* Wakeup Failed */
AD5940_SleepKeyCtrlS(SLPKEY_LOCK); /* Prohibit AFE to enter sleep mode. */
if(AD5940_INTCTestFlag(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH) == bTRUE)
{
/* Now there should be 4 data in FIFO */
FifoCnt = (AD5940_FIFOGetCnt()/4)*4;
if(FifoCnt > BuffCount)
{
///@todo buffer is limited.
}
AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
AppIMPRegModify(pBuff, &FifoCnt); /* If there is need to do AFE re-configure, do it here when AFE is in active state */
//AD5940_EnterSleepS(); /* Manually put AFE back to hibernate mode. This operation only takes effect when register value is ACTIVE previously */
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); /* Allow AFE to enter sleep mode. */
/* Process data */
AppIMPDataProcess((int32_t*)pBuff,&FifoCnt);
*pCount = FifoCnt;
return 0;
}
return 0;
}

View File

@ -1,3 +1,4 @@
// File: host/src/MainWindow.cpp
#include "MainWindow.h"
#include <QVBoxLayout>
#include <QDebug>
@ -10,7 +11,7 @@
#include <QTimer>
#include <cmath>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), currentSweepIndex(0), isSweeping(false) {
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
serial = new QSerialPort(this);
connect(serial, &QSerialPort::readyRead, this, &MainWindow::handleSerialData);
connect(serial, &QSerialPort::errorOccurred, this, &MainWindow::onPortError);
@ -18,7 +19,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), currentSweepIndex
setupUi();
// Delayed refresh to allow the window to render before auto-scanning
QTimer::singleShot(1000, this, &MainWindow::refreshPorts);
generateSweepPoints();
// Enable Swipe Gestures for Mobile Tab Switching
grabGesture(Qt::SwipeGesture);
@ -103,10 +103,13 @@ void MainWindow::setupUi() {
connect(calibrateBtn, &QPushButton::clicked, this, &MainWindow::runCalibration);
connect(sweepBtn, &QPushButton::clicked, this, &MainWindow::startSweep);
connect(measureBtn, &QPushButton::clicked, [this]() {
// Measure Button Logic
connect(measureBtn, &QPushButton::clicked, [this, spinFreq]() {
if (serial->isOpen()) {
logWidget->append(">> Requesting Measure (m)...");
serial->write("m");
double freq = spinFreq->value();
logWidget->append(QString(">> Requesting Measure (m %1)...").arg(freq));
// Send 'm <freq>\n'
serial->write(QString("m %1\n").arg(freq).toUtf8());
}
});
@ -149,7 +152,6 @@ void MainWindow::connectToPort() {
checkIdBtn->setEnabled(false);
calibrateBtn->setEnabled(false);
sweepBtn->setEnabled(false);
isSweeping = false;
return;
}
@ -177,42 +179,33 @@ void MainWindow::onPortError(QSerialPort::SerialPortError error) {
checkIdBtn->setEnabled(false);
calibrateBtn->setEnabled(false);
sweepBtn->setEnabled(false);
isSweeping = false;
}
}
void MainWindow::checkDeviceId() {
if (serial->isOpen()) {
logWidget->append(">> Checking ID (v)...");
serial->write("v");
serial->write("v\n");
}
}
void MainWindow::runCalibration() {
if (serial->isOpen()) {
logWidget->append(">> Running Calibration (c)...");
serial->write("c");
serial->write("c\n");
}
}
void MainWindow::startSweep() {
if (!serial->isOpen()) return;
isSweeping = true;
currentSweepIndex = 0;
finalGraph->clear();
rawGraph->clear();
logWidget->append(">> Starting Ratio-metric Sweep...");
sendNextSweepPoint();
}
void MainWindow::sendNextSweepPoint() {
if (!isSweeping || !serial->isOpen()) return;
if (currentSweepIndex >= sweepPoints.size()) {
isSweeping = false;
logWidget->append(">> Sweep Complete.");
return;
}
serial->write("m");
// Use Firmware Sweep Command: s <start> <end> <steps>
// Example: 100Hz to 200kHz, 50 steps
logWidget->append(">> Starting Firmware Sweep (s 100 200000 50)...");
serial->write("s 100 200000 50\n");
}
void MainWindow::handleSerialData() {
@ -232,6 +225,7 @@ void MainWindow::handleSerialData() {
}
void MainWindow::parseData(const QString &data) {
// Format: DATA,Freq,Mag,Phase,Real,Imag
QStringList parts = data.split(',');
if (parts.size() < 6) return;
@ -244,24 +238,6 @@ void MainWindow::parseData(const QString &data) {
if (okF && okM && okP) finalGraph->addData(freq, mag, phase);
if (okF && okR && okI) rawGraph->addData(freq, real, imag);
if (isSweeping) {
currentSweepIndex++;
QTimer::singleShot(50, this, &MainWindow::sendNextSweepPoint);
}
}
void MainWindow::generateSweepPoints() {
sweepPoints.clear();
double startFreq = 100.0;
double endFreq = 200000.0;
int steps = 50;
double logStart = std::log10(startFreq);
double logEnd = std::log10(endFreq);
double logStep = (logEnd - logStart) / (steps - 1);
for (int i = 0; i < steps; ++i) {
sweepPoints.append(std::pow(10, logStart + i * logStep));
}
}
bool MainWindow::event(QEvent *event) {

View File

@ -1,3 +1,4 @@
// File: host/src/MainWindow.h
#pragma once
#include <QMainWindow>
@ -35,13 +36,11 @@ private slots:
void checkDeviceId();
void runCalibration();
void startSweep();
void sendNextSweepPoint();
private:
void setupUi();
void parseData(const QString &data);
void handleSwipe(QSwipeGesture *gesture);
void generateSweepPoints();
QSerialPort *serial;
@ -58,9 +57,4 @@ private:
QPushButton *checkIdBtn;
QPushButton *calibrateBtn;
QPushButton *sweepBtn;
// Sweep Logic
QList<double> sweepPoints;
int currentSweepIndex;
bool isSweeping;
};

399
main.c
View File

@ -1,257 +1,272 @@
// File: main.c
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "hardware/watchdog.h"
#include "ad5940.h"
#include "Impedance.h"
// Pin Definitions
// ---------------------------------------------------------------------------
// Hardware Definitions
// ---------------------------------------------------------------------------
#define PIN_MISO 0
#define PIN_CS 1
#define PIN_SCK 2
#define PIN_MOSI 3
#define PIN_RST 9
#define PIN_INT 29
#define PIN_INT 29
#define RCAL_VALUE 100.0f
#define APPBUFF_SIZE 512
uint32_t AppBuff[APPBUFF_SIZE];
// ---------------------------------------------------------------------------
// Platform Interface Implementation (Required by AD5940 Lib)
// Platform Interface Implementation
// ---------------------------------------------------------------------------
void AD5940_CsClr(void) {
gpio_put(PIN_CS, 0);
}
void AD5940_CsSet(void) {
gpio_put(PIN_CS, 1);
}
void AD5940_RstClr(void) {
gpio_put(PIN_RST, 0);
}
void AD5940_RstSet(void) {
gpio_put(PIN_RST, 1);
}
void AD5940_Delay10us(uint32_t time) {
sleep_us(time * 10);
}
void AD5940_CsClr(void) { gpio_put(PIN_CS, 0); }
void AD5940_CsSet(void) { gpio_put(PIN_CS, 1); }
void AD5940_RstClr(void) { gpio_put(PIN_RST, 0); }
void AD5940_RstSet(void) { gpio_put(PIN_RST, 1); }
void AD5940_Delay10us(uint32_t time) { sleep_us(time * 10); }
void AD5940_ReadWriteNBytes(unsigned char *pSendBuffer, unsigned char *pRecvBuff, unsigned long length) {
spi_write_read_blocking(spi0, pSendBuffer, pRecvBuff, length);
}
uint32_t AD5940_GetMCUIntFlag(void) {
return (gpio_get(PIN_INT) == 0);
}
uint32_t AD5940_ClrMCUIntFlag(void) {
return 1;
}
uint32_t AD5940_MCUResourceInit(void *pCfg) {
return 0;
}
void AD5940_MCUGpioWrite(uint32_t data) { (void)data; }
uint32_t AD5940_MCUGpioRead(uint32_t pin) { (void)pin; return 0; }
void AD5940_MCUGpioCtrl(uint32_t pin, BoolFlag enable) { (void)pin; (void)enable; }
// ---------------------------------------------------------------------------
// Application Logic
// ---------------------------------------------------------------------------
void setup_pins(void) {
// SPI0 at 1MHz
spi_init(spi0, 1000000);
// Increase SPI speed to 4MHz to prevent FIFO overflow during fast sweeps
spi_init(spi0, 4000000);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
// CS
gpio_init(PIN_CS);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);
// RST
gpio_init(PIN_RST);
gpio_set_dir(PIN_RST, GPIO_OUT);
gpio_put(PIN_RST, 1);
// INT
gpio_init(PIN_INT);
gpio_set_dir(PIN_INT, GPIO_IN);
gpio_pull_up(PIN_INT);
gpio_pull_up(PIN_INT);
}
void configure_afe(void) {
HSDACCfg_Type hsdac_cfg;
HSTIACfg_Type hsrtia_cfg;
HSLoopCfg_Type hsloop_cfg;
void AD5940ImpedanceStructInit(void)
{
AppIMPCfg_Type *pImpedanceCfg;
AppIMPGetCfg(&pImpedanceCfg);
pImpedanceCfg->SeqStartAddr = 0;
pImpedanceCfg->MaxSeqLen = 512;
// Reset AFE control
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
pImpedanceCfg->RcalVal = 10000.0;
pImpedanceCfg->RtiaVal = 200.0;
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_TRTIA;
// Configure High Speed DAC
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_2;
hsdac_cfg.HsDacGain = HSDACGAIN_1;
hsdac_cfg.HsDacUpdateRate = 0x1B;
AD5940_HSDacCfgS(&hsdac_cfg);
pImpedanceCfg->HstiaRtiaSel = HSTIARTIA_200;
pImpedanceCfg->BiasVolt = 0.0;
// Configure High Speed TIA
hsrtia_cfg.HstiaDeRtia = HSTIADERTIA_1K;
hsrtia_cfg.HstiaCtia = 31; // 31pF
hsrtia_cfg.HstiaRtiaSel = HSTIARTIA_1K;
hsrtia_cfg.HstiaBias = HSTIABIAS_1P1;
hsrtia_cfg.DiodeClose = bFALSE;
hsrtia_cfg.HstiaDeRload = HSTIADERLOAD_OPEN;
AD5940_HSTIACfgS(&hsrtia_cfg);
pImpedanceCfg->SweepCfg.SweepEn = bFALSE;
pImpedanceCfg->SweepCfg.SweepStart = 100.0f;
pImpedanceCfg->SweepCfg.SweepStop = 100000.0f;
pImpedanceCfg->SweepCfg.SweepPoints = 50;
pImpedanceCfg->SweepCfg.SweepLog = bTRUE;
// Configure High Speed Loop
hsloop_cfg.HsDacCfg = hsdac_cfg;
hsloop_cfg.HsTiaCfg = hsrtia_cfg;
hsloop_cfg.SWMatCfg.Dswitch = SWD_OPEN;
hsloop_cfg.SWMatCfg.Pswitch = SWP_OPEN;
hsloop_cfg.SWMatCfg.Nswitch = SWN_OPEN;
hsloop_cfg.SWMatCfg.Tswitch = SWT_OPEN;
hsloop_cfg.WgCfg.WgType = WGTYPE_SIN;
hsloop_cfg.WgCfg.GainCalEn = bFALSE;
hsloop_cfg.WgCfg.OffsetCalEn = bFALSE;
hsloop_cfg.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(1000.0, 16000000.0);
hsloop_cfg.WgCfg.SinCfg.SinAmplitudeWord = 2047;
hsloop_cfg.WgCfg.SinCfg.SinOffsetWord = 0;
hsloop_cfg.WgCfg.SinCfg.SinPhaseWord = 0;
AD5940_HSLoopCfgS(&hsloop_cfg);
// Enable Power
AD5940_AFECtrlS(AFECTRL_ADCPWR | AFECTRL_HSTIAPWR | AFECTRL_HSDACPWR | AFECTRL_HPREFPWR, bTRUE);
// Enable Interrupts
AD5940_WriteReg(REG_INTC_INTCSEL0, BITM_INTC_INTCSEL0_INTSEL14);
pImpedanceCfg->PwrMod = AFEPWR_LP;
pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_4;
pImpedanceCfg->DftNum = DFTNUM_16384;
pImpedanceCfg->DftSrc = DFTSRC_SINC3;
}
float perform_dft(void) {
iImpCar_Type dft_res;
fImpCar_Type f_res;
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bTRUE);
// Wait for interrupt or timeout
uint32_t timeout = 50000;
while(gpio_get(PIN_INT) && timeout--) {
sleep_us(10);
static int32_t AD5940PlatformCfg(void)
{
CLKCfg_Type clk_cfg;
FIFOCfg_Type fifo_cfg;
AGPIOCfg_Type gpio_cfg;
AD5940_HWReset();
AD5940_Initialize();
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
clk_cfg.SysClkDiv = SYSCLKDIV_1;
clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
clk_cfg.HfOSC32MHzMode = bFALSE;
clk_cfg.HFOSCEn = bTRUE;
clk_cfg.HFXTALEn = bFALSE;
clk_cfg.LFOSCEn = bTRUE;
AD5940_CLKCfg(&clk_cfg);
fifo_cfg.FIFOEn = bFALSE;
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
fifo_cfg.FIFOThresh = 6;
AD5940_FIFOCfg(&fifo_cfg);
fifo_cfg.FIFOEn = bTRUE;
AD5940_FIFOCfg(&fifo_cfg);
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH, bTRUE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
gpio_cfg.FuncSet = GP0_INT;
gpio_cfg.InputEnSet = 0;
gpio_cfg.OutputEnSet = AGPIO_Pin0;
gpio_cfg.OutVal = 0;
gpio_cfg.PullEnSet = 0;
AD5940_AGPIOCfg(&gpio_cfg);
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
return 0;
}
void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
{
float freq;
fImpPol_Type *pImp = (fImpPol_Type*)pData;
AppIMPCtrl(IMPCTRL_GETFREQ, &freq);
for(int i=0;i<DataCount;i++)
{
printf("DATA,%.2f,%.4f,%.4f,%.4f,%.4f\n",
freq,
pImp[i].Magnitude,
pImp[i].Phase*180/MATH_PI,
pImp[i].Magnitude,
pImp[i].Phase
);
}
}
// ---------------------------------------------------------------------------
// Main Loop
// ---------------------------------------------------------------------------
char input_buffer[64];
int input_pos = 0;
void process_command() {
char cmd = input_buffer[0];
AppIMPCfg_Type *pCfg;
AppIMPGetCfg(&pCfg);
// CRITICAL: Stop any running measurement before reconfiguration
AppIMPCtrl(IMPCTRL_STOPNOW, 0);
if (cmd == 'v') {
uint32_t id = AD5940_ReadReg(REG_AFECON_CHIPID);
printf("CHIP_ID:0x%04X\n", id);
}
else if (cmd == 'c') {
pCfg->SweepCfg.SweepEn = bFALSE;
pCfg->SinFreq = 1000.0f;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppIMPCtrl(IMPCTRL_START, 0);
} else {
printf("ERROR: Init Failed\n");
}
}
dft_res.Real = (int32_t)AD5940_ReadReg(REG_AFE_DFTREAL);
dft_res.Image = (int32_t)AD5940_ReadReg(REG_AFE_DFTIMAG);
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bFALSE);
AD5940_WriteReg(REG_INTC_INTCFLAG0, BITM_INTC_INTCFLAG0_FLAG14);
f_res.Real = (float)dft_res.Real;
f_res.Image = (float)dft_res.Image;
return AD5940_ComplexMag(&f_res);
}
void set_sw_matrix(uint32_t lp_sw, uint32_t hs_sw) {
AD5940_WriteReg(REG_AFE_LPTIASW0, lp_sw);
AD5940_WriteReg(REG_AFE_HSTIACON, hs_sw);
}
void measure_at_freq(float freq) {
FreqParams_Type freq_params = AD5940_GetFreqParameters(freq);
// 1. Update HSDAC Update Rate based on frequency
uint32_t hsdac_rate = 0x1B;
if(freq >= 80000.0f) {
hsdac_rate = 0x07;
else if (cmd == 'm') {
float freq = 1000.0f;
if (strlen(input_buffer) > 2) freq = atof(input_buffer + 2);
pCfg->SweepCfg.SweepEn = bFALSE;
pCfg->SinFreq = freq;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppIMPCtrl(IMPCTRL_START, 0);
} else {
printf("ERROR: Init Failed\n");
}
}
uint32_t hsdaccon = AD5940_ReadReg(REG_AFE_HSDACCON);
hsdaccon &= ~BITM_AFE_HSDACCON_RATE;
hsdaccon |= (hsdac_rate << BITP_AFE_HSDACCON_RATE);
AD5940_WriteReg(REG_AFE_HSDACCON, hsdaccon);
// 2. Update Waveform Generator Frequency
uint32_t freq_word = AD5940_WGFreqWordCal(freq, 16000000.0f);
AD5940_WriteReg(REG_AFE_WGFCW, freq_word);
// 3. Update Filter Settings
ADCFilterCfg_Type filter_cfg;
// Initialize struct to 0 to avoid garbage
filter_cfg.ADCSinc3Osr = freq_params.ADCSinc3Osr;
filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr;
filter_cfg.ADCAvgNum = ADCAVGNUM_16;
filter_cfg.ADCRate = ADCRATE_800KHZ;
filter_cfg.BpNotch = bTRUE;
filter_cfg.BpSinc3 = bFALSE;
filter_cfg.Sinc2NotchEnable = bTRUE;
filter_cfg.DFTClkEnable = bTRUE;
filter_cfg.WGClkEnable = bTRUE;
filter_cfg.Sinc3ClkEnable = bTRUE;
filter_cfg.Sinc2NotchClkEnable = bTRUE;
AD5940_ADCFilterCfgS(&filter_cfg);
DFTCfg_Type dft_cfg;
dft_cfg.DftNum = freq_params.DftNum;
dft_cfg.DftSrc = freq_params.DftSrc;
dft_cfg.HanWinEn = bTRUE;
AD5940_DFTCfgS(&dft_cfg);
// 4. Perform Measurement
// RCAL
set_sw_matrix(0, SWP_RCAL0 | SWN_RCAL1);
sleep_ms(5);
float mag_cal = perform_dft();
// Z
set_sw_matrix(0, SWP_CE0 | SWP_RE0 | SWN_SE0 | SWT_DE0);
sleep_ms(5);
float mag_z = perform_dft();
if (mag_z > 0.01f && mag_cal > 0.01f) {
float impedance = (mag_cal / mag_z) * RCAL_VALUE;
printf("DATA,%.2f,%.2f,0,0,0\n", freq, impedance);
} else {
printf("DATA,%.2f,0,0,0,0\n", freq);
else if (cmd == 's') {
float start = 100.0f, end = 100000.0f;
int steps = 50;
if (strlen(input_buffer) > 2) sscanf(input_buffer + 2, "%f %f %d", &start, &end, &steps);
pCfg->SweepCfg.SweepEn = bTRUE;
pCfg->SweepCfg.SweepStart = start;
pCfg->SweepCfg.SweepStop = end;
pCfg->SweepCfg.SweepPoints = steps;
pCfg->SweepCfg.SweepLog = bTRUE;
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
AppIMPCtrl(IMPCTRL_START, 0);
} else {
printf("ERROR: Init Failed\n");
}
}
}
void run_sweep(float start_freq, float end_freq, int steps) {
float log_start = log10f(start_freq);
float log_end = log10f(end_freq);
float step_size = (steps > 1) ? (log_end - log_start) / (steps - 1) : 0;
printf("START_SWEEP\n");
for (int i = 0; i < steps; ++i) {
float freq = powf(10.0f, log_start + (i * step_size));
measure_at_freq(freq);
else if (cmd == 'z') {
// Full System Reset
watchdog_reboot(0, 0, 0);
}
printf("END_SWEEP\n");
}
void system_init(void) {
setup_pins();
// Hardware Reset AD5940
AD5940_RstClr();
sleep_ms(10);
AD5940_RstSet();
sleep_ms(10);
AD5940_Initialize();
configure_afe();
}
int main() {
stdio_init_all();
sleep_ms(2000);
system_init();
setup_pins();
AD5940PlatformCfg();
AD5940ImpedanceStructInit();
printf("SYSTEM_READY\n");
while (true) {
int c = getchar_timeout_us(100);
if (c == 'm') {
run_sweep(100.0f, 100000.0f, 50);
int c = getchar_timeout_us(0);
if (c != PICO_ERROR_TIMEOUT) {
if (c == '\n' || c == '\r') {
input_buffer[input_pos] = 0;
if (input_pos > 0) process_command();
input_pos = 0;
} else if (input_pos < 63) {
input_buffer[input_pos++] = (char)c;
}
}
if (c == 'z') {
system_init();
printf("RESET_DONE\n");
if (gpio_get(PIN_INT) == 0) {
uint32_t temp = APPBUFF_SIZE;
AppIMPISR(AppBuff, &temp);
if(temp > 0) {
ImpedanceShowResult(AppBuff, temp);
}
}
}
return 0;