EIS/Amperometric.c

486 lines
16 KiB
C

// File: Amperometric.c
#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*/
.ShortRe0Se0 = bFALSE,
};
/**
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);
}
// Apply Short Option
if(AppAMPCfg.ShortRe0Se0) {
lp_loop.LpAmpCfg.LpTiaSW |= LPTIASW(11);
}
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;
}